make swfdec_as_object_mark() only mark if not marked yet
[swfdec.git] / swfdec / swfdec_sprite_movie.c
blobf64dca2c76f9eeb1f0e826f0d621d7aca49a4f07
1 /* Swfdec
2 * Copyright (C) 2006-2008 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 "swfdec_sprite_movie.h"
25 #include "swfdec_as_internal.h"
26 #include "swfdec_as_strings.h"
27 #include "swfdec_audio_swf_stream.h"
28 #include "swfdec_audio_event.h"
29 #include "swfdec_audio_stream.h"
30 #include "swfdec_debug.h"
31 #include "swfdec_filter.h"
32 #include "swfdec_graphic_movie.h"
33 #include "swfdec_player_internal.h"
34 #include "swfdec_ringbuffer.h"
35 #include "swfdec_script_internal.h"
36 #include "swfdec_sprite.h"
37 #include "swfdec_resource.h"
38 #include "swfdec_sandbox.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_gc_object_get_context (movie));
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 if (SWFDEC_IS_ACTOR (cur)) {
125 SwfdecActor *actor = SWFDEC_ACTOR (cur);
126 swfdec_actor_queue_script (actor, SWFDEC_EVENT_INITIALIZE);
127 swfdec_actor_queue_script (actor, SWFDEC_EVENT_CONSTRUCT);
128 swfdec_actor_queue_script (actor, SWFDEC_EVENT_LOAD);
130 swfdec_movie_initialize (cur);
132 return TRUE;
136 static gboolean
137 swfdec_sprite_movie_perform_place (SwfdecSpriteMovie *movie, SwfdecBits *bits, guint tag)
139 SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (movie));
140 SwfdecMovie *mov = SWFDEC_MOVIE (movie);
141 SwfdecMovie *cur;
142 SwfdecSwfDecoder *dec;
143 gboolean has_clip_actions;
144 gboolean has_clip_depth;
145 gboolean has_name;
146 gboolean has_ratio;
147 gboolean has_ctrans;
148 gboolean has_transform;
149 gboolean has_character;
150 gboolean move;
151 int depth;
152 gboolean cache;
153 gboolean has_blend_mode = 0;
154 gboolean has_filter = 0;
155 int clip_depth;
156 cairo_matrix_t transform;
157 SwfdecColorTransform ctrans;
158 guint ratio, id, version;
159 SwfdecEventList *events;
160 const char *name;
161 guint blend_mode;
162 SwfdecGraphic *graphic;
163 GSList *filters;
165 dec = SWFDEC_SWF_DECODER (mov->resource->decoder);
166 version = dec->version;
168 /* 1) check which stuff is set */
169 has_clip_actions = swfdec_bits_getbit (bits);
170 has_clip_depth = swfdec_bits_getbit (bits);
171 has_name = swfdec_bits_getbit (bits);
172 has_ratio = swfdec_bits_getbit (bits);
173 has_ctrans = swfdec_bits_getbit (bits);
174 has_transform = swfdec_bits_getbit (bits);
175 has_character = swfdec_bits_getbit (bits);
176 move = swfdec_bits_getbit (bits);
178 SWFDEC_LOG ("performing PlaceObject%d on movie %s", tag == SWFDEC_TAG_PLACEOBJECT2 ? 2 : 3, mov->name);
179 SWFDEC_LOG (" has_clip_actions = %d", has_clip_actions);
180 SWFDEC_LOG (" has_clip_depth = %d", has_clip_depth);
181 SWFDEC_LOG (" has_name = %d", has_name);
182 SWFDEC_LOG (" has_ratio = %d", has_ratio);
183 SWFDEC_LOG (" has_ctrans = %d", has_ctrans);
184 SWFDEC_LOG (" has_transform = %d", has_transform);
185 SWFDEC_LOG (" has_character = %d", has_character);
186 SWFDEC_LOG (" move = %d", move);
188 if (tag == SWFDEC_TAG_PLACEOBJECT3) {
189 swfdec_bits_getbits (bits, 5);
190 cache = swfdec_bits_getbit (bits);
191 has_blend_mode = swfdec_bits_getbit (bits);
192 has_filter = swfdec_bits_getbit (bits);
193 SWFDEC_LOG (" cache = %d", cache);
194 SWFDEC_LOG (" has filter = %d", has_filter);
195 SWFDEC_LOG (" has blend mode = %d", has_blend_mode);
198 /* 2) read all properties */
199 depth = swfdec_bits_get_u16 (bits);
200 if (depth >= 16384) {
201 SWFDEC_FIXME ("depth of placement too high: %u >= 16384", depth);
203 SWFDEC_LOG (" depth = %d (=> %d)", depth, depth - 16384);
204 depth -= 16384;
205 if (has_character) {
206 id = swfdec_bits_get_u16 (bits);
207 SWFDEC_LOG (" id = %d", id);
208 } else {
209 id = 0;
212 if (has_transform) {
213 swfdec_bits_get_matrix (bits, &transform, NULL);
214 SWFDEC_LOG (" matrix = { %g %g, %g %g } + { %g %g }",
215 transform.xx, transform.yx,
216 transform.xy, transform.yy,
217 transform.x0, transform.y0);
219 if (has_ctrans) {
220 swfdec_bits_get_color_transform (bits, &ctrans);
221 SWFDEC_LOG (" color transform = %d %d %d %d %d %d %d %d",
222 ctrans.ra, ctrans.rb,
223 ctrans.ga, ctrans.gb,
224 ctrans.ba, ctrans.bb,
225 ctrans.aa, ctrans.ab);
228 if (has_ratio) {
229 ratio = swfdec_bits_get_u16 (bits);
230 SWFDEC_LOG (" ratio = %d", ratio);
231 } else {
232 ratio = -1;
235 if (has_name) {
236 char *s = swfdec_bits_get_string (bits, version);
237 if (s) {
238 name = swfdec_as_context_give_string (SWFDEC_AS_CONTEXT (player), s);
239 SWFDEC_LOG (" name = %s", name);
240 } else {
241 name = NULL;
243 } else {
244 name = NULL;
247 if (has_clip_depth) {
248 clip_depth = swfdec_bits_get_u16 (bits) - 16384;
249 SWFDEC_LOG (" clip_depth = %d (=> %d)", clip_depth + 16384, clip_depth);
250 } else {
251 clip_depth = 0;
254 if (has_filter) {
255 filters = swfdec_filter_parse (player, bits);
256 } else {
257 filters = NULL;
260 if (has_blend_mode) {
261 blend_mode = swfdec_bits_get_u8 (bits);
262 SWFDEC_LOG (" blend mode = %u", blend_mode);
263 } else {
264 blend_mode = 0;
267 if (has_clip_actions) {
268 int reserved, clip_event_flags, event_flags, key_code;
269 char *script_name;
271 events = swfdec_event_list_new ();
272 reserved = swfdec_bits_get_u16 (bits);
273 clip_event_flags = swfdec_get_clipeventflags (mov, bits);
275 if (name)
276 script_name = g_strdup (name);
277 else if (id)
278 script_name = g_strdup_printf ("Sprite%u", id);
279 else
280 script_name = g_strdup ("unknown");
281 while ((event_flags = swfdec_get_clipeventflags (mov, bits)) != 0) {
282 guint length = swfdec_bits_get_u32 (bits);
283 SwfdecBits action_bits;
285 swfdec_bits_init_bits (&action_bits, bits, length);
286 if (event_flags & (1<<SWFDEC_EVENT_KEY_PRESS))
287 key_code = swfdec_bits_get_u8 (&action_bits);
288 else
289 key_code = 0;
291 SWFDEC_INFO ("clip event with flags 0x%X, key code %d", event_flags, key_code);
292 #define SWFDEC_UNIMPLEMENTED_EVENTS \
293 ((1<< SWFDEC_EVENT_DATA))
294 if (event_flags & SWFDEC_UNIMPLEMENTED_EVENTS) {
295 SWFDEC_ERROR ("using non-implemented clip events %u", event_flags & SWFDEC_UNIMPLEMENTED_EVENTS);
297 swfdec_event_list_parse (events, &action_bits, version,
298 event_flags, key_code, script_name);
299 if (swfdec_bits_left (&action_bits)) {
300 SWFDEC_ERROR ("not all action data was parsed: %u bytes left",
301 swfdec_bits_left (&action_bits));
304 g_free (script_name);
305 } else {
306 events = NULL;
309 /* 3) perform the actions depending on the set properties */
310 cur = swfdec_movie_find (mov, depth);
311 graphic = swfdec_swf_decoder_get_character (dec, id);
312 if (move) {
313 if (cur == NULL) {
314 SWFDEC_INFO ("no movie at depth %d, ignoring move command", depth);
315 goto out;
317 if (graphic) {
318 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (cur);
319 if (klass->replace)
320 klass->replace (cur, graphic);
322 swfdec_movie_set_static_properties (cur, has_transform ? &transform : NULL,
323 has_ctrans ? &ctrans : NULL, ratio, clip_depth, blend_mode, events);
324 } else {
325 if (cur != NULL && version > 5) {
326 SWFDEC_INFO ("depth %d is already occupied by movie %s, not placing", depth, cur->name);
327 goto out;
329 if (!SWFDEC_IS_GRAPHIC (graphic)) {
330 SWFDEC_FIXME ("character %u is not a graphic (does it even exist?), aborting", id);
331 if (events)
332 swfdec_event_list_free (events);
333 g_slist_foreach (filters, (GFunc) g_object_unref, NULL);
334 g_slist_free (filters);
335 return FALSE;
337 cur = swfdec_movie_new (player, depth, mov, mov->resource, graphic, name);
338 swfdec_movie_set_static_properties (cur, has_transform ? &transform : NULL,
339 has_ctrans ? &ctrans : NULL, ratio, clip_depth, blend_mode, events);
340 if (SWFDEC_IS_ACTOR (cur)) {
341 SwfdecActor *actor = SWFDEC_ACTOR (cur);
342 swfdec_actor_queue_script (actor, SWFDEC_EVENT_INITIALIZE);
343 swfdec_actor_queue_script (actor, SWFDEC_EVENT_CONSTRUCT);
344 swfdec_actor_queue_script (actor, SWFDEC_EVENT_LOAD);
346 swfdec_movie_initialize (cur);
348 out:
349 if (has_filter) {
350 if (cur->filters)
351 g_slist_free (cur->filters);
352 swfdec_movie_invalidate_next (cur);
353 cur->filters = filters;
356 if (events)
357 swfdec_event_list_free (events);
358 return TRUE;
361 static void
362 swfdec_sprite_movie_start_sound (SwfdecMovie *movie, SwfdecBits *bits)
364 SwfdecSoundChunk *chunk;
365 int id;
367 id = swfdec_bits_get_u16 (bits);
368 chunk = swfdec_sound_parse_chunk (SWFDEC_SWF_DECODER (movie->resource->decoder), bits, id);
369 if (chunk) {
370 SwfdecAudio *audio = swfdec_audio_event_new_from_chunk (SWFDEC_PLAYER (
371 swfdec_gc_object_get_context (movie)), chunk);
372 if (audio)
373 g_object_unref (audio);
377 static gboolean
378 swfdec_sprite_movie_perform_one_action (SwfdecSpriteMovie *movie, guint tag, SwfdecBuffer *buffer,
379 gboolean fast_forward, gboolean first_time)
381 SwfdecMovie *mov = SWFDEC_MOVIE (movie);
382 SwfdecActor *actor = SWFDEC_ACTOR (movie);
383 SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (mov));
384 SwfdecBits bits;
386 g_assert (mov->resource);
387 swfdec_bits_init (&bits, buffer);
389 SWFDEC_LOG ("%p: executing %uth tag %s in frame %u", movie, movie->next_action - 1,
390 swfdec_swf_decoder_get_tag_name (tag), movie->frame);
391 switch (tag) {
392 case SWFDEC_TAG_SETBACKGROUNDCOLOR:
393 swfdec_player_set_background_color (player, swfdec_bits_get_color (&bits));
394 return TRUE;
395 case SWFDEC_TAG_DOACTION:
396 SWFDEC_LOG ("SCRIPT action");
397 if (!fast_forward) {
398 SwfdecScript *script = swfdec_swf_decoder_get_script (
399 SWFDEC_SWF_DECODER (mov->resource->decoder), buffer->data);
400 if (script) {
401 swfdec_player_add_action_script (player, actor, script,
402 SWFDEC_PLAYER_ACTION_QUEUE_NORMAL);
403 } else {
404 SWFDEC_ERROR ("Failed to locate script for DoAction tag");
407 return TRUE;
408 case SWFDEC_TAG_PLACEOBJECT:
409 return swfdec_sprite_movie_perform_old_place (movie, &bits, tag);
410 case SWFDEC_TAG_PLACEOBJECT2:
411 case SWFDEC_TAG_PLACEOBJECT3:
412 return swfdec_sprite_movie_perform_place (movie, &bits, tag);
413 case SWFDEC_TAG_REMOVEOBJECT:
414 /* yes, this code is meant to be like this - the following u16 is the
415 * character id, that we don't care about, the rest is like RemoveObject2
417 swfdec_bits_get_u16 (&bits);
418 /* fall through */
419 case SWFDEC_TAG_REMOVEOBJECT2:
421 int depth = swfdec_bits_get_u16 (&bits);
422 SWFDEC_LOG ("REMOVE action: depth %d => %d", depth, depth - 16384);
423 depth -= 16384;
424 if (!swfdec_sprite_movie_remove_child (mov, depth))
425 SWFDEC_INFO ("could not remove, no child at depth %d", depth);
427 return TRUE;
428 case SWFDEC_TAG_STARTSOUND:
429 if (!fast_forward)
430 swfdec_sprite_movie_start_sound (mov, &bits);
431 return TRUE;
432 case SWFDEC_TAG_SHOWFRAME:
433 if (movie->frame < movie->n_frames) {
434 movie->frame++;
435 } else {
436 SWFDEC_ERROR ("too many ShowFrame tags");
438 return FALSE;
439 case SWFDEC_TAG_EXPORTASSETS:
441 SwfdecResource *resource = swfdec_movie_get_own_resource (mov);
442 guint i, count;
444 g_assert (resource); /* must hold, ExportAssets can only be in root movies */
445 if (!first_time)
446 return TRUE;
447 count = swfdec_bits_get_u16 (&bits);
448 SWFDEC_LOG ("exporting %u assets", count);
449 for (i = 0; i < count && swfdec_bits_left (&bits); i++) {
450 SwfdecSwfDecoder *s = SWFDEC_SWF_DECODER (resource->decoder);
451 guint id;
452 SwfdecCharacter *object;
453 char *name;
455 id = swfdec_bits_get_u16 (&bits);
456 object = swfdec_swf_decoder_get_character (s, id);
457 name = swfdec_bits_get_string (&bits, s->version);
458 if (object == NULL) {
459 SWFDEC_ERROR ("cannot export id %u as %s, id wasn't found", id, name);
460 } else if (name == NULL) {
461 SWFDEC_ERROR ("cannot export id %u, no name was given", id);
462 } else {
463 SWFDEC_LOG ("exporting %s %u as %s", G_OBJECT_TYPE_NAME (object), id, name);
464 swfdec_resource_add_export (resource, object, name);
466 g_free (name);
469 return TRUE;
470 case SWFDEC_TAG_DOINITACTION:
471 if (!first_time)
472 return TRUE;
473 if (!swfdec_movie_get_own_resource (mov)) {
474 SWFDEC_FIXME ("behavior of init actions in DefineSprite untested");
477 guint id;
478 SwfdecSprite *sprite;
480 id = swfdec_bits_get_u16 (&bits);
481 SWFDEC_LOG ("InitAction");
482 SWFDEC_LOG (" id = %u", id);
483 sprite = swfdec_swf_decoder_get_character (SWFDEC_SWF_DECODER (mov->resource->decoder), id);
484 if (!SWFDEC_IS_SPRITE (sprite)) {
485 SWFDEC_ERROR ("character %u is not a sprite", id);
486 return TRUE;
488 if (sprite->init_action != NULL) {
489 SWFDEC_ERROR ("sprite %u already has an init action", id);
490 return TRUE;
492 sprite->init_action = swfdec_script_ref (swfdec_swf_decoder_get_script (
493 SWFDEC_SWF_DECODER (mov->resource->decoder), buffer->data + 2));
494 if (sprite->init_action) {
495 swfdec_player_add_action_script (player, actor, sprite->init_action,
496 SWFDEC_PLAYER_ACTION_QUEUE_INIT);
497 } else {
498 SWFDEC_ERROR ("Failed to locate script for InitAction of Sprite %u", id);
501 return TRUE;
502 case SWFDEC_TAG_SOUNDSTREAMHEAD:
503 case SWFDEC_TAG_SOUNDSTREAMHEAD2:
504 /* ignore, those are handled by the sound stream */
505 return TRUE;
506 case SWFDEC_TAG_SOUNDSTREAMBLOCK:
507 if (!fast_forward) {
508 if (movie->sound_stream == NULL) {
509 movie->sound_stream = swfdec_audio_swf_stream_new (player, movie->sprite,
510 movie->next_action - 1);
512 movie->sound_active = TRUE;
514 return TRUE;
515 default:
516 g_assert_not_reached ();
517 return FALSE;
521 static gboolean
522 swfdec_movie_is_compatible (SwfdecMovie *movie, SwfdecMovie *with)
524 g_assert (movie->depth == with->depth);
526 if (movie->original_ratio != with->original_ratio)
527 return FALSE;
529 if (G_OBJECT_TYPE (movie) != G_OBJECT_TYPE (with))
530 return FALSE;
532 return TRUE;
535 static GList *
536 my_g_list_split (GList *list, GList *split)
538 GList *prev;
540 if (split == NULL)
541 return list;
543 prev = split->prev;
544 if (prev == NULL)
545 return NULL;
546 prev->next = NULL;
547 split->prev = NULL;
548 return list;
551 void
552 swfdec_sprite_movie_goto (SwfdecSpriteMovie *movie, guint goto_frame)
554 SwfdecMovie *mov;
555 SwfdecPlayer *player;
556 GList *old;
557 guint n;
558 gboolean remove_audio;
560 g_return_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie));
562 mov = SWFDEC_MOVIE (movie);
563 /* lots of things where we've got nothing to do */
564 if (goto_frame == 0 || goto_frame > movie->n_frames ||
565 movie->sprite == NULL || mov->state >= SWFDEC_MOVIE_STATE_REMOVED || goto_frame == movie->frame)
566 return;
568 if (goto_frame > movie->sprite->parse_frame) {
569 SWFDEC_WARNING ("jumping to not-yet-loaded frame %u (loaded: %u/%u)",
570 goto_frame, movie->sprite->parse_frame, movie->sprite->n_frames);
571 return;
574 player = SWFDEC_PLAYER (swfdec_gc_object_get_context (movie));
575 SWFDEC_LOG ("doing goto %u for %p %d", goto_frame, movie,
576 SWFDEC_CHARACTER (movie->sprite)->id);
578 SWFDEC_DEBUG ("performing goto %u -> %u for character %u",
579 movie->frame, goto_frame, SWFDEC_CHARACTER (movie->sprite)->id);
580 if (goto_frame < movie->frame) {
581 GList *walk;
582 movie->frame = 0;
583 for (walk = mov->list; walk &&
584 swfdec_depth_classify (SWFDEC_MOVIE (walk->data)->depth) != SWFDEC_DEPTH_CLASS_TIMELINE;
585 walk = walk->next) {
586 /* do nothing */
588 old = walk;
589 mov->list = my_g_list_split (mov->list, old);
590 for (walk = old; walk &&
591 swfdec_depth_classify (SWFDEC_MOVIE (walk->data)->depth) == SWFDEC_DEPTH_CLASS_TIMELINE;
592 walk = walk->next) {
593 /* do nothing */
595 old = my_g_list_split (old, walk);
596 mov->list = g_list_concat (mov->list, walk);
597 n = goto_frame;
598 movie->next_action = 0;
599 remove_audio = TRUE;
600 } else {
601 /* NB: this path is also taken on init */
602 old = NULL;
603 n = goto_frame - movie->frame;
604 remove_audio = n > 1;
606 /* remove audio after seeks */
607 if (remove_audio && movie->sound_stream) {
608 swfdec_audio_remove (movie->sound_stream);
609 g_object_unref (movie->sound_stream);
610 movie->sound_stream = NULL;
612 remove_audio = !movie->sound_active;
613 movie->sound_active = FALSE;
614 while (n) {
615 guint tag;
616 gboolean first_time;
617 SwfdecBuffer *buffer;
618 if (!swfdec_sprite_get_action (movie->sprite, movie->next_action, &tag, &buffer))
619 break;
620 movie->next_action++;
621 if (movie->next_action > movie->max_action) {
622 first_time = TRUE;
623 movie->max_action = movie->next_action;
624 } else {
625 first_time = FALSE;
627 if (!swfdec_sprite_movie_perform_one_action (movie, tag, buffer, n > 1, first_time))
628 n--;
630 /* now try to copy eventual movies */
631 if (old) {
632 SwfdecMovie *prev, *cur;
633 GList *old_walk, *walk;
634 walk = mov->list;
635 old_walk = old;
636 if (!walk)
637 goto out;
638 cur = walk->data;
639 for (; old_walk; old_walk = old_walk->next) {
640 prev = old_walk->data;
641 while (cur->depth < prev->depth) {
642 walk = walk->next;
643 if (!walk)
644 goto out;
645 cur = walk->data;
647 if (cur->depth == prev->depth &&
648 swfdec_movie_is_compatible (prev, cur)) {
649 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (prev);
650 walk->data = prev;
651 /* FIXME: This merging stuff probably needs to be improved a _lot_ */
652 if (klass->replace)
653 klass->replace (prev, cur->graphic);
654 swfdec_movie_set_static_properties (prev, &cur->original_transform,
655 &cur->color_transform, cur->original_ratio, cur->clip_depth,
656 cur->blend_mode, SWFDEC_IS_ACTOR (cur) ? SWFDEC_ACTOR (cur)->events : NULL);
657 swfdec_movie_destroy (cur);
658 cur = prev;
659 continue;
661 swfdec_movie_remove (prev);
663 out:
664 for (; old_walk; old_walk = old_walk->next) {
665 swfdec_movie_remove (old_walk->data);
667 g_list_free (old);
670 /* after two frames without SoundStreamBlock, audio apparently gets removed */
671 if (!movie->sound_active && remove_audio && movie->sound_stream != NULL) {
672 swfdec_audio_remove (movie->sound_stream);
673 g_object_unref (movie->sound_stream);
674 movie->sound_stream = NULL;
678 /*** MOVIE ***/
680 G_DEFINE_TYPE (SwfdecSpriteMovie, swfdec_sprite_movie, SWFDEC_TYPE_ACTOR)
682 static void
683 swfdec_sprite_movie_dispose (GObject *object)
685 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (object);
687 g_assert (movie->sound_stream == NULL);
689 G_OBJECT_CLASS (swfdec_sprite_movie_parent_class)->dispose (object);
692 static void
693 swfdec_sprite_movie_init_movie (SwfdecMovie *mov)
695 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
697 g_assert (movie->frame == (guint) -1);
698 movie->frame = 0;
699 swfdec_sprite_movie_goto (movie, 1);
702 static GObject *
703 swfdec_sprite_movie_constructor (GType type, guint n_construct_properties,
704 GObjectConstructParam *construct_properties)
706 GObject *object;
707 SwfdecMovie *movie;
709 object = G_OBJECT_CLASS (swfdec_sprite_movie_parent_class)->constructor (type,
710 n_construct_properties, construct_properties);
712 movie = SWFDEC_MOVIE (object);
713 if (movie->resource->sandbox) {
714 /* FIXME: This hack is probably wrong */
715 SwfdecAsObject *o = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie));
716 if (swfdec_sandbox_try_use (movie->resource->sandbox)) {
717 swfdec_as_object_set_constructor_by_name (o,
718 SWFDEC_AS_STR_MovieClip, NULL);
719 swfdec_sandbox_unuse (movie->resource->sandbox);
720 } else {
721 swfdec_as_object_set_constructor_by_name (o,
722 SWFDEC_AS_STR_MovieClip, NULL);
726 if (movie->graphic) {
727 SwfdecSpriteMovie *smovie = SWFDEC_SPRITE_MOVIE (object);
728 smovie->sprite = SWFDEC_SPRITE (movie->graphic);
729 smovie->n_frames = smovie->sprite->n_frames;
732 return object;
735 static void
736 swfdec_sprite_movie_iterate (SwfdecActor *actor)
738 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (actor);
739 guint goto_frame;
741 if (SWFDEC_MOVIE (actor)->state >= SWFDEC_MOVIE_STATE_REMOVED)
742 return;
744 if (movie->sprite && movie->frame == (guint) -1)
745 movie->frame = 0;
747 swfdec_actor_queue_script (actor, SWFDEC_EVENT_ENTER);
748 if (movie->playing && movie->sprite != NULL) {
749 if (movie->frame == movie->n_frames)
750 goto_frame = 1;
751 else if (movie->sprite && movie->frame == movie->sprite->parse_frame)
752 goto_frame = movie->frame;
753 else
754 goto_frame = movie->frame + 1;
755 swfdec_sprite_movie_goto (movie, goto_frame);
759 static void
760 swfdec_sprite_movie_finish_movie (SwfdecMovie *mov)
762 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
763 SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (mov));
765 swfdec_player_remove_all_actions (player, SWFDEC_ACTOR (mov));
766 if (movie->sound_stream) {
767 swfdec_audio_remove (movie->sound_stream);
768 g_object_unref (movie->sound_stream);
769 movie->sound_stream = NULL;
773 static void
774 swfdec_sprite_movie_property_get (SwfdecMovie *mov, guint prop_id, SwfdecAsValue *val)
776 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
777 SwfdecAsContext *cx = swfdec_gc_object_get_context (mov);
779 switch (prop_id) {
780 case SWFDEC_MOVIE_PROPERTY_CURRENTFRAME:
781 swfdec_as_value_set_integer (cx, val, movie->frame);
782 break;
783 case SWFDEC_MOVIE_PROPERTY_FRAMESLOADED:
784 swfdec_as_value_set_integer (cx, val, swfdec_sprite_movie_get_frames_loaded (movie));
785 break;
786 case SWFDEC_MOVIE_PROPERTY_TOTALFRAMES:
787 swfdec_as_value_set_integer (cx, val, swfdec_sprite_movie_get_frames_total (movie));
788 break;
789 default:
790 SWFDEC_MOVIE_CLASS (swfdec_sprite_movie_parent_class)->property_get (mov, prop_id, val);
791 break;
795 static void
796 swfdec_sprite_movie_class_init (SwfdecSpriteMovieClass * g_class)
798 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
799 SwfdecMovieClass *movie_class = SWFDEC_MOVIE_CLASS (g_class);
800 SwfdecActorClass *actor_class = SWFDEC_ACTOR_CLASS (g_class);
802 object_class->dispose = swfdec_sprite_movie_dispose;
803 object_class->constructor = swfdec_sprite_movie_constructor;
805 movie_class->init_movie = swfdec_sprite_movie_init_movie;
806 movie_class->finish_movie = swfdec_sprite_movie_finish_movie;
807 movie_class->property_get = swfdec_sprite_movie_property_get;
809 actor_class->iterate_start = swfdec_sprite_movie_iterate;
812 static void
813 swfdec_sprite_movie_init (SwfdecSpriteMovie * movie)
815 movie->playing = TRUE;
816 movie->frame = (guint) -1;
820 * swfdec_sprite_movie_get_frames_loaded:
821 * @movie: a #SwfdecSpriteMovie
823 * Computes the number of loaded frames as used by the _framesloaded property
824 * or the WaitForFrame actions. If the @movie is fully loaded, this is the
825 * amount of total frames of the sprite it displays, or 0 if it has no sprite.
826 * If the movie is not fully loaded, it is the amount of frames that are
827 * completely loaded minus one. Welcome to the world of Flash.
829 * Returns: The number of loaded frames as reported by ActionScript.
832 swfdec_sprite_movie_get_frames_loaded (SwfdecSpriteMovie *movie)
834 SwfdecResource *resource;
835 SwfdecDecoder *dec;
837 g_return_val_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie), 0);
839 resource = swfdec_movie_get_own_resource (SWFDEC_MOVIE (movie));
840 if (resource == NULL) {
841 /* FIXME: can we set n_frames to 1 for movies without sprites instead? */
842 if (movie->sprite)
843 return movie->n_frames;
844 else
845 return 1;
847 dec = resource->decoder;
848 if (dec == NULL)
849 return -1;
850 if (dec->frames_loaded < dec->frames_total)
851 return dec->frames_loaded - 1;
852 return dec->frames_total;
856 swfdec_sprite_movie_get_frames_total (SwfdecSpriteMovie *movie)
858 SwfdecResource *resource;
859 SwfdecDecoder *dec;
861 g_return_val_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie), 0);
863 resource = swfdec_movie_get_own_resource (SWFDEC_MOVIE (movie));
864 if (resource == NULL) {
865 /* FIXME: can we set n_frames to 1 for movies without sprites instead? */
866 if (movie->sprite)
867 return movie->n_frames;
868 else
869 return 1;
871 dec = resource->decoder;
872 if (dec == NULL)
873 return 0;
874 return dec->frames_total;