release 0.8.0
[swfdec.git] / swfdec / swfdec_sprite_movie.c
blob451cdfa6eda9522c445d65f2adc2714f9220789e
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_tag.h"
40 /*** SWFDEC_SPRITE_MOVIE ***/
42 static gboolean
43 swfdec_sprite_movie_remove_child (SwfdecMovie *movie, int depth)
45 SwfdecMovie *child = swfdec_movie_find (movie, depth);
47 if (child == NULL)
48 return FALSE;
50 swfdec_movie_remove (child);
51 return TRUE;
54 static int
55 swfdec_get_clipeventflags (SwfdecMovie *movie, SwfdecBits * bits)
57 if (SWFDEC_SWF_DECODER (movie->resource->decoder)->version <= 5) {
58 return swfdec_bits_get_u16 (bits);
59 } else {
60 return swfdec_bits_get_u32 (bits);
64 static gboolean
65 swfdec_sprite_movie_perform_old_place (SwfdecSpriteMovie *movie,
66 SwfdecBits *bits, guint tag)
68 SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (movie));
69 SwfdecMovie *mov = SWFDEC_MOVIE (movie);
70 SwfdecMovie *cur;
71 SwfdecSwfDecoder *dec;
72 int depth;
73 cairo_matrix_t transform;
74 gboolean has_ctrans;
75 SwfdecColorTransform ctrans;
76 guint id;
77 SwfdecGraphic *graphic;
79 dec = SWFDEC_SWF_DECODER (mov->resource->decoder);
81 SWFDEC_LOG ("performing PlaceObject on movie %s", mov->name);
83 id = swfdec_bits_get_u16 (bits);
84 SWFDEC_LOG (" id = %d", id);
86 depth = swfdec_bits_get_u16 (bits);
87 if (depth >= 16384) {
88 SWFDEC_FIXME ("depth of placement too high: %u >= 16384", depth);
90 SWFDEC_LOG (" depth = %d (=> %d)", depth, depth - 16384);
91 depth -= 16384;
93 swfdec_bits_get_matrix (bits, &transform, NULL);
94 SWFDEC_LOG (" matrix = { %g %g, %g %g } + { %g %g }",
95 transform.xx, transform.yx,
96 transform.xy, transform.yy,
97 transform.x0, transform.y0);
99 if (swfdec_bits_left (bits)) {
100 has_ctrans = TRUE;
101 swfdec_bits_get_color_transform (bits, &ctrans);
102 SWFDEC_LOG (" color transform = %d %d %d %d %d %d %d %d",
103 ctrans.ra, ctrans.rb,
104 ctrans.ga, ctrans.gb,
105 ctrans.ba, ctrans.bb,
106 ctrans.aa, ctrans.ab);
107 } else {
108 has_ctrans = FALSE;
111 /* 3) perform the actions depending on the set properties */
112 cur = swfdec_movie_find (mov, depth);
113 graphic = swfdec_swf_decoder_get_character (dec, id);
115 if (!SWFDEC_IS_GRAPHIC (graphic)) {
116 SWFDEC_FIXME ("character %u is not a graphic (does it even exist?), aborting", id);
117 return FALSE;
120 cur = swfdec_movie_new (player, depth, mov, mov->resource, graphic, NULL);
121 swfdec_movie_set_static_properties (cur, &transform,
122 has_ctrans ? &ctrans : NULL, -1, 0, 0, NULL);
123 if (SWFDEC_IS_ACTOR (cur)) {
124 SwfdecActor *actor = SWFDEC_ACTOR (cur);
125 swfdec_actor_queue_script (actor, SWFDEC_EVENT_INITIALIZE);
126 swfdec_actor_queue_script (actor, SWFDEC_EVENT_CONSTRUCT);
127 swfdec_actor_queue_script (actor, SWFDEC_EVENT_LOAD);
129 swfdec_movie_initialize (cur);
131 return TRUE;
135 static gboolean
136 swfdec_sprite_movie_perform_place (SwfdecSpriteMovie *movie, SwfdecBits *bits, guint tag)
138 SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (movie));
139 SwfdecMovie *mov = SWFDEC_MOVIE (movie);
140 SwfdecMovie *cur;
141 SwfdecSwfDecoder *dec;
142 gboolean has_clip_actions;
143 gboolean has_clip_depth;
144 gboolean has_name;
145 gboolean has_ratio;
146 gboolean has_ctrans;
147 gboolean has_transform;
148 gboolean has_character;
149 gboolean move;
150 int depth;
151 gboolean cache;
152 gboolean has_blend_mode = 0;
153 gboolean has_filter = 0;
154 int clip_depth;
155 cairo_matrix_t transform;
156 SwfdecColorTransform ctrans;
157 guint ratio, id, version;
158 SwfdecEventList *events;
159 const char *name;
160 guint blend_mode;
161 SwfdecGraphic *graphic;
163 dec = SWFDEC_SWF_DECODER (mov->resource->decoder);
164 version = dec->version;
166 /* 1) check which stuff is set */
167 has_clip_actions = swfdec_bits_getbit (bits);
168 has_clip_depth = swfdec_bits_getbit (bits);
169 has_name = swfdec_bits_getbit (bits);
170 has_ratio = swfdec_bits_getbit (bits);
171 has_ctrans = swfdec_bits_getbit (bits);
172 has_transform = swfdec_bits_getbit (bits);
173 has_character = swfdec_bits_getbit (bits);
174 move = swfdec_bits_getbit (bits);
176 SWFDEC_LOG ("performing PlaceObject%d on movie %s", tag == SWFDEC_TAG_PLACEOBJECT2 ? 2 : 3, mov->name);
177 SWFDEC_LOG (" has_clip_actions = %d", has_clip_actions);
178 SWFDEC_LOG (" has_clip_depth = %d", has_clip_depth);
179 SWFDEC_LOG (" has_name = %d", has_name);
180 SWFDEC_LOG (" has_ratio = %d", has_ratio);
181 SWFDEC_LOG (" has_ctrans = %d", has_ctrans);
182 SWFDEC_LOG (" has_transform = %d", has_transform);
183 SWFDEC_LOG (" has_character = %d", has_character);
184 SWFDEC_LOG (" move = %d", move);
186 if (tag == SWFDEC_TAG_PLACEOBJECT3) {
187 swfdec_bits_getbits (bits, 5);
188 cache = swfdec_bits_getbit (bits);
189 has_blend_mode = swfdec_bits_getbit (bits);
190 has_filter = swfdec_bits_getbit (bits);
191 SWFDEC_LOG (" cache = %d", cache);
192 SWFDEC_LOG (" has filter = %d", has_filter);
193 SWFDEC_LOG (" has blend mode = %d", has_blend_mode);
196 /* 2) read all properties */
197 depth = swfdec_bits_get_u16 (bits);
198 if (depth >= 16384) {
199 SWFDEC_FIXME ("depth of placement too high: %u >= 16384", depth);
201 SWFDEC_LOG (" depth = %d (=> %d)", depth, depth - 16384);
202 depth -= 16384;
203 if (has_character) {
204 id = swfdec_bits_get_u16 (bits);
205 SWFDEC_LOG (" id = %d", id);
206 } else {
207 id = 0;
210 if (has_transform) {
211 swfdec_bits_get_matrix (bits, &transform, NULL);
212 SWFDEC_LOG (" matrix = { %g %g, %g %g } + { %g %g }",
213 transform.xx, transform.yx,
214 transform.xy, transform.yy,
215 transform.x0, transform.y0);
217 if (has_ctrans) {
218 swfdec_bits_get_color_transform (bits, &ctrans);
219 SWFDEC_LOG (" color transform = %d %d %d %d %d %d %d %d",
220 ctrans.ra, ctrans.rb,
221 ctrans.ga, ctrans.gb,
222 ctrans.ba, ctrans.bb,
223 ctrans.aa, ctrans.ab);
226 if (has_ratio) {
227 ratio = swfdec_bits_get_u16 (bits);
228 SWFDEC_LOG (" ratio = %d", ratio);
229 } else {
230 ratio = -1;
233 if (has_name) {
234 char *s = swfdec_bits_get_string (bits, version);
235 if (s) {
236 name = swfdec_as_context_give_string (SWFDEC_AS_CONTEXT (player), s);
237 SWFDEC_LOG (" name = %s", name);
238 } else {
239 name = NULL;
241 } else {
242 name = NULL;
245 if (has_clip_depth) {
246 clip_depth = swfdec_bits_get_u16 (bits) - 16384;
247 SWFDEC_LOG (" clip_depth = %d (=> %d)", clip_depth + 16384, clip_depth);
248 } else {
249 clip_depth = 0;
252 if (has_filter) {
253 GSList *filters = swfdec_filter_parse (bits);
254 g_slist_free (filters);
257 if (has_blend_mode) {
258 blend_mode = swfdec_bits_get_u8 (bits);
259 SWFDEC_LOG (" blend mode = %u", blend_mode);
260 } else {
261 blend_mode = 0;
264 if (has_clip_actions) {
265 int reserved, clip_event_flags, event_flags, key_code;
266 char *script_name;
268 events = swfdec_event_list_new ();
269 reserved = swfdec_bits_get_u16 (bits);
270 clip_event_flags = swfdec_get_clipeventflags (mov, bits);
272 if (name)
273 script_name = g_strdup (name);
274 else if (id)
275 script_name = g_strdup_printf ("Sprite%u", id);
276 else
277 script_name = g_strdup ("unknown");
278 while ((event_flags = swfdec_get_clipeventflags (mov, bits)) != 0) {
279 guint length = swfdec_bits_get_u32 (bits);
280 SwfdecBits action_bits;
282 swfdec_bits_init_bits (&action_bits, bits, length);
283 if (event_flags & (1<<SWFDEC_EVENT_KEY_PRESS))
284 key_code = swfdec_bits_get_u8 (&action_bits);
285 else
286 key_code = 0;
288 SWFDEC_INFO ("clip event with flags 0x%X, key code %d", event_flags, key_code);
289 #define SWFDEC_UNIMPLEMENTED_EVENTS \
290 ((1<< SWFDEC_EVENT_DATA))
291 if (event_flags & SWFDEC_UNIMPLEMENTED_EVENTS) {
292 SWFDEC_ERROR ("using non-implemented clip events %u", event_flags & SWFDEC_UNIMPLEMENTED_EVENTS);
294 swfdec_event_list_parse (events, &action_bits, version,
295 event_flags, key_code, script_name);
296 if (swfdec_bits_left (&action_bits)) {
297 SWFDEC_ERROR ("not all action data was parsed: %u bytes left",
298 swfdec_bits_left (&action_bits));
301 g_free (script_name);
302 } else {
303 events = NULL;
306 /* 3) perform the actions depending on the set properties */
307 cur = swfdec_movie_find (mov, depth);
308 graphic = swfdec_swf_decoder_get_character (dec, id);
309 if (move) {
310 if (cur == NULL) {
311 SWFDEC_INFO ("no movie at depth %d, ignoring move command", depth);
312 goto out;
314 if (graphic) {
315 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (cur);
316 if (klass->replace)
317 klass->replace (cur, graphic);
319 swfdec_movie_set_static_properties (cur, has_transform ? &transform : NULL,
320 has_ctrans ? &ctrans : NULL, ratio, clip_depth, blend_mode, events);
321 } else {
322 if (cur != NULL && version > 5) {
323 SWFDEC_INFO ("depth %d is already occupied by movie %s, not placing", depth, cur->name);
324 goto out;
326 if (!SWFDEC_IS_GRAPHIC (graphic)) {
327 SWFDEC_FIXME ("character %u is not a graphic (does it even exist?), aborting", id);
328 if (events)
329 swfdec_event_list_free (events);
330 return FALSE;
332 cur = swfdec_movie_new (player, depth, mov, mov->resource, graphic, name);
333 swfdec_movie_set_static_properties (cur, has_transform ? &transform : NULL,
334 has_ctrans ? &ctrans : NULL, ratio, clip_depth, blend_mode, events);
335 if (SWFDEC_IS_ACTOR (cur)) {
336 SwfdecActor *actor = SWFDEC_ACTOR (cur);
337 swfdec_actor_queue_script (actor, SWFDEC_EVENT_INITIALIZE);
338 swfdec_actor_queue_script (actor, SWFDEC_EVENT_CONSTRUCT);
339 swfdec_actor_queue_script (actor, SWFDEC_EVENT_LOAD);
341 swfdec_movie_initialize (cur);
344 out:
345 if (events)
346 swfdec_event_list_free (events);
347 return TRUE;
350 static void
351 swfdec_sprite_movie_start_sound (SwfdecMovie *movie, SwfdecBits *bits)
353 SwfdecSoundChunk *chunk;
354 int id;
356 id = swfdec_bits_get_u16 (bits);
357 chunk = swfdec_sound_parse_chunk (SWFDEC_SWF_DECODER (movie->resource->decoder), bits, id);
358 if (chunk) {
359 SwfdecAudio *audio = swfdec_audio_event_new_from_chunk (SWFDEC_PLAYER (
360 swfdec_gc_object_get_context (movie)), chunk);
361 if (audio)
362 g_object_unref (audio);
366 static gboolean
367 swfdec_sprite_movie_perform_one_action (SwfdecSpriteMovie *movie, guint tag, SwfdecBuffer *buffer,
368 gboolean fast_forward, gboolean first_time)
370 SwfdecMovie *mov = SWFDEC_MOVIE (movie);
371 SwfdecActor *actor = SWFDEC_ACTOR (movie);
372 SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (mov));
373 SwfdecBits bits;
375 g_assert (mov->resource);
376 swfdec_bits_init (&bits, buffer);
378 SWFDEC_LOG ("%p: executing %uth tag %s in frame %u", movie, movie->next_action - 1,
379 swfdec_swf_decoder_get_tag_name (tag), movie->frame);
380 switch (tag) {
381 case SWFDEC_TAG_SETBACKGROUNDCOLOR:
382 swfdec_player_set_background_color (player, swfdec_bits_get_color (&bits));
383 return TRUE;
384 case SWFDEC_TAG_DOACTION:
385 SWFDEC_LOG ("SCRIPT action");
386 if (!fast_forward) {
387 SwfdecScript *script = swfdec_swf_decoder_get_script (
388 SWFDEC_SWF_DECODER (mov->resource->decoder), buffer->data);
389 if (script) {
390 swfdec_player_add_action_script (player, actor, script,
391 SWFDEC_PLAYER_ACTION_QUEUE_NORMAL);
392 } else {
393 SWFDEC_ERROR ("Failed to locate script for DoAction tag");
396 return TRUE;
397 case SWFDEC_TAG_PLACEOBJECT:
398 return swfdec_sprite_movie_perform_old_place (movie, &bits, tag);
399 case SWFDEC_TAG_PLACEOBJECT2:
400 case SWFDEC_TAG_PLACEOBJECT3:
401 return swfdec_sprite_movie_perform_place (movie, &bits, tag);
402 case SWFDEC_TAG_REMOVEOBJECT:
403 /* yes, this code is meant to be like this - the following u16 is the
404 * character id, that we don't care about, the rest is like RemoveObject2
406 swfdec_bits_get_u16 (&bits);
407 /* fall through */
408 case SWFDEC_TAG_REMOVEOBJECT2:
410 int depth = swfdec_bits_get_u16 (&bits);
411 SWFDEC_LOG ("REMOVE action: depth %d => %d", depth, depth - 16384);
412 depth -= 16384;
413 if (!swfdec_sprite_movie_remove_child (mov, depth))
414 SWFDEC_INFO ("could not remove, no child at depth %d", depth);
416 return TRUE;
417 case SWFDEC_TAG_STARTSOUND:
418 if (!fast_forward)
419 swfdec_sprite_movie_start_sound (mov, &bits);
420 return TRUE;
421 case SWFDEC_TAG_SHOWFRAME:
422 if (movie->frame < movie->n_frames) {
423 movie->frame++;
424 } else {
425 SWFDEC_ERROR ("too many ShowFrame tags");
427 return FALSE;
428 case SWFDEC_TAG_EXPORTASSETS:
430 SwfdecResource *resource = swfdec_movie_get_own_resource (mov);
431 guint i, count;
433 g_assert (resource); /* must hold, ExportAssets can only be in root movies */
434 if (!first_time)
435 return TRUE;
436 count = swfdec_bits_get_u16 (&bits);
437 SWFDEC_LOG ("exporting %u assets", count);
438 for (i = 0; i < count && swfdec_bits_left (&bits); i++) {
439 SwfdecSwfDecoder *s = SWFDEC_SWF_DECODER (resource->decoder);
440 guint id;
441 SwfdecCharacter *object;
442 char *name;
444 id = swfdec_bits_get_u16 (&bits);
445 object = swfdec_swf_decoder_get_character (s, id);
446 name = swfdec_bits_get_string (&bits, s->version);
447 if (object == NULL) {
448 SWFDEC_ERROR ("cannot export id %u as %s, id wasn't found", id, name);
449 } else if (name == NULL) {
450 SWFDEC_ERROR ("cannot export id %u, no name was given", id);
451 } else {
452 SWFDEC_LOG ("exporting %s %u as %s", G_OBJECT_TYPE_NAME (object), id, name);
453 swfdec_resource_add_export (resource, object, name);
455 g_free (name);
458 return TRUE;
459 case SWFDEC_TAG_DOINITACTION:
460 if (!first_time)
461 return TRUE;
462 if (!swfdec_movie_get_own_resource (mov)) {
463 SWFDEC_FIXME ("behavior of init actions in DefineSprite untested");
466 guint id;
467 SwfdecSprite *sprite;
469 id = swfdec_bits_get_u16 (&bits);
470 SWFDEC_LOG ("InitAction");
471 SWFDEC_LOG (" id = %u", id);
472 sprite = swfdec_swf_decoder_get_character (SWFDEC_SWF_DECODER (mov->resource->decoder), id);
473 if (!SWFDEC_IS_SPRITE (sprite)) {
474 SWFDEC_ERROR ("character %u is not a sprite", id);
475 return TRUE;
477 if (sprite->init_action != NULL) {
478 SWFDEC_ERROR ("sprite %u already has an init action", id);
479 return TRUE;
481 sprite->init_action = swfdec_script_ref (swfdec_swf_decoder_get_script (
482 SWFDEC_SWF_DECODER (mov->resource->decoder), buffer->data + 2));
483 if (sprite->init_action) {
484 swfdec_player_add_action_script (player, actor, sprite->init_action,
485 SWFDEC_PLAYER_ACTION_QUEUE_INIT);
486 } else {
487 SWFDEC_ERROR ("Failed to locate script for InitAction of Sprite %u", id);
490 return TRUE;
491 case SWFDEC_TAG_SOUNDSTREAMHEAD:
492 case SWFDEC_TAG_SOUNDSTREAMHEAD2:
493 /* ignore, those are handled by the sound stream */
494 return TRUE;
495 case SWFDEC_TAG_SOUNDSTREAMBLOCK:
496 if (!fast_forward) {
497 if (movie->sound_stream == NULL) {
498 movie->sound_stream = swfdec_audio_swf_stream_new (player, movie->sprite,
499 movie->next_action - 1);
501 movie->sound_active = TRUE;
503 return TRUE;
504 default:
505 g_assert_not_reached ();
506 return FALSE;
510 static gboolean
511 swfdec_movie_is_compatible (SwfdecMovie *movie, SwfdecMovie *with)
513 g_assert (movie->depth == with->depth);
515 if (movie->original_ratio != with->original_ratio)
516 return FALSE;
518 if (G_OBJECT_TYPE (movie) != G_OBJECT_TYPE (with))
519 return FALSE;
521 return TRUE;
524 static GList *
525 my_g_list_split (GList *list, GList *split)
527 GList *prev;
529 if (split == NULL)
530 return list;
532 prev = split->prev;
533 if (prev == NULL)
534 return NULL;
535 prev->next = NULL;
536 split->prev = NULL;
537 return list;
540 void
541 swfdec_sprite_movie_goto (SwfdecSpriteMovie *movie, guint goto_frame)
543 SwfdecMovie *mov;
544 SwfdecPlayer *player;
545 GList *old;
546 guint n;
547 gboolean remove_audio;
549 g_return_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie));
551 mov = SWFDEC_MOVIE (movie);
552 /* lots of things where we've got nothing to do */
553 if (goto_frame == 0 || goto_frame > movie->n_frames ||
554 movie->sprite == NULL || mov->state >= SWFDEC_MOVIE_STATE_REMOVED || goto_frame == movie->frame)
555 return;
557 if (goto_frame > movie->sprite->parse_frame) {
558 SWFDEC_WARNING ("jumping to not-yet-loaded frame %u (loaded: %u/%u)",
559 goto_frame, movie->sprite->parse_frame, movie->sprite->n_frames);
560 return;
563 player = SWFDEC_PLAYER (swfdec_gc_object_get_context (movie));
564 SWFDEC_LOG ("doing goto %u for %p %d", goto_frame, movie,
565 SWFDEC_CHARACTER (movie->sprite)->id);
567 SWFDEC_DEBUG ("performing goto %u -> %u for character %u",
568 movie->frame, goto_frame, SWFDEC_CHARACTER (movie->sprite)->id);
569 if (goto_frame < movie->frame) {
570 GList *walk;
571 movie->frame = 0;
572 for (walk = mov->list; walk &&
573 swfdec_depth_classify (SWFDEC_MOVIE (walk->data)->depth) != SWFDEC_DEPTH_CLASS_TIMELINE;
574 walk = walk->next) {
575 /* do nothing */
577 old = walk;
578 mov->list = my_g_list_split (mov->list, old);
579 for (walk = old; walk &&
580 swfdec_depth_classify (SWFDEC_MOVIE (walk->data)->depth) == SWFDEC_DEPTH_CLASS_TIMELINE;
581 walk = walk->next) {
582 /* do nothing */
584 old = my_g_list_split (old, walk);
585 mov->list = g_list_concat (mov->list, walk);
586 n = goto_frame;
587 movie->next_action = 0;
588 remove_audio = TRUE;
589 } else {
590 /* NB: this path is also taken on init */
591 old = NULL;
592 n = goto_frame - movie->frame;
593 remove_audio = n > 1;
595 /* remove audio after seeks */
596 if (remove_audio && movie->sound_stream) {
597 swfdec_audio_remove (movie->sound_stream);
598 g_object_unref (movie->sound_stream);
599 movie->sound_stream = NULL;
601 remove_audio = !movie->sound_active;
602 movie->sound_active = FALSE;
603 while (n) {
604 guint tag;
605 gboolean first_time;
606 SwfdecBuffer *buffer;
607 if (!swfdec_sprite_get_action (movie->sprite, movie->next_action, &tag, &buffer))
608 break;
609 movie->next_action++;
610 if (movie->next_action > movie->max_action) {
611 first_time = TRUE;
612 movie->max_action = movie->next_action;
613 } else {
614 first_time = FALSE;
616 if (!swfdec_sprite_movie_perform_one_action (movie, tag, buffer, n > 1, first_time))
617 n--;
619 /* now try to copy eventual movies */
620 if (old) {
621 SwfdecMovie *prev, *cur;
622 GList *old_walk, *walk;
623 walk = mov->list;
624 old_walk = old;
625 if (!walk)
626 goto out;
627 cur = walk->data;
628 for (; old_walk; old_walk = old_walk->next) {
629 prev = old_walk->data;
630 while (cur->depth < prev->depth) {
631 walk = walk->next;
632 if (!walk)
633 goto out;
634 cur = walk->data;
636 if (cur->depth == prev->depth &&
637 swfdec_movie_is_compatible (prev, cur)) {
638 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (prev);
639 walk->data = prev;
640 /* FIXME: This merging stuff probably needs to be improved a _lot_ */
641 if (klass->replace)
642 klass->replace (prev, cur->graphic);
643 swfdec_movie_set_static_properties (prev, &cur->original_transform,
644 &cur->color_transform, cur->original_ratio, cur->clip_depth,
645 cur->blend_mode, SWFDEC_IS_ACTOR (cur) ? SWFDEC_ACTOR (cur)->events : NULL);
646 swfdec_movie_destroy (cur);
647 cur = prev;
648 continue;
650 swfdec_movie_remove (prev);
652 out:
653 for (; old_walk; old_walk = old_walk->next) {
654 swfdec_movie_remove (old_walk->data);
656 g_list_free (old);
659 /* after two frames without SoundStreamBlock, audio apparently gets removed */
660 if (!movie->sound_active && remove_audio && movie->sound_stream != NULL) {
661 swfdec_audio_remove (movie->sound_stream);
662 g_object_unref (movie->sound_stream);
663 movie->sound_stream = NULL;
667 /*** MOVIE ***/
669 G_DEFINE_TYPE (SwfdecSpriteMovie, swfdec_sprite_movie, SWFDEC_TYPE_ACTOR)
671 static void
672 swfdec_sprite_movie_dispose (GObject *object)
674 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (object);
676 g_assert (movie->sound_stream == NULL);
678 G_OBJECT_CLASS (swfdec_sprite_movie_parent_class)->dispose (object);
681 static void
682 swfdec_sprite_movie_init_movie (SwfdecMovie *mov)
684 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
686 g_assert (movie->frame == (guint) -1);
687 movie->frame = 0;
688 swfdec_sprite_movie_goto (movie, 1);
691 static GObject *
692 swfdec_sprite_movie_constructor (GType type, guint n_construct_properties,
693 GObjectConstructParam *construct_properties)
695 GObject *object;
696 SwfdecMovie *movie;
698 object = G_OBJECT_CLASS (swfdec_sprite_movie_parent_class)->constructor (type,
699 n_construct_properties, construct_properties);
701 movie = SWFDEC_MOVIE (object);
702 if (movie->resource->sandbox) {
703 swfdec_as_object_set_constructor (SWFDEC_AS_OBJECT (movie),
704 movie->resource->sandbox->MovieClip);
707 if (movie->graphic) {
708 SwfdecSpriteMovie *smovie = SWFDEC_SPRITE_MOVIE (object);
709 smovie->sprite = SWFDEC_SPRITE (movie->graphic);
710 smovie->n_frames = smovie->sprite->n_frames;
713 return object;
716 static void
717 swfdec_sprite_movie_iterate (SwfdecActor *actor)
719 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (actor);
720 guint goto_frame;
722 if (SWFDEC_MOVIE (actor)->state >= SWFDEC_MOVIE_STATE_REMOVED)
723 return;
725 if (movie->sprite && movie->frame == (guint) -1)
726 movie->frame = 0;
728 swfdec_actor_queue_script (actor, SWFDEC_EVENT_ENTER);
729 if (movie->playing && movie->sprite != NULL) {
730 if (movie->frame == movie->n_frames)
731 goto_frame = 1;
732 else if (movie->sprite && movie->frame == movie->sprite->parse_frame)
733 goto_frame = movie->frame;
734 else
735 goto_frame = movie->frame + 1;
736 swfdec_sprite_movie_goto (movie, goto_frame);
740 static void
741 swfdec_sprite_movie_finish_movie (SwfdecMovie *mov)
743 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
744 SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (mov));
746 swfdec_player_remove_all_actions (player, SWFDEC_ACTOR (mov));
747 if (movie->sound_stream) {
748 swfdec_audio_remove (movie->sound_stream);
749 g_object_unref (movie->sound_stream);
750 movie->sound_stream = NULL;
754 static void
755 swfdec_sprite_movie_mark (SwfdecGcObject *object)
757 GList *walk;
759 for (walk = SWFDEC_MOVIE (object)->list; walk; walk = walk->next) {
760 SwfdecAsObject *child = walk->data;
761 g_assert (child->properties != NULL);
762 swfdec_gc_object_mark (child);
765 SWFDEC_GC_OBJECT_CLASS (swfdec_sprite_movie_parent_class)->mark (object);
768 static void
769 swfdec_sprite_movie_property_get (SwfdecMovie *mov, guint prop_id, SwfdecAsValue *val)
771 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (mov);
773 switch (prop_id) {
774 case SWFDEC_MOVIE_PROPERTY_CURRENTFRAME:
775 SWFDEC_AS_VALUE_SET_INT (val, movie->frame);
776 break;
777 case SWFDEC_MOVIE_PROPERTY_FRAMESLOADED:
778 SWFDEC_AS_VALUE_SET_INT (val, swfdec_sprite_movie_get_frames_loaded (movie));
779 break;
780 case SWFDEC_MOVIE_PROPERTY_TOTALFRAMES:
781 SWFDEC_AS_VALUE_SET_INT (val, swfdec_sprite_movie_get_frames_total (movie));
782 break;
783 default:
784 SWFDEC_MOVIE_CLASS (swfdec_sprite_movie_parent_class)->property_get (mov, prop_id, val);
785 break;
789 static void
790 swfdec_sprite_movie_class_init (SwfdecSpriteMovieClass * g_class)
792 GObjectClass *object_class = G_OBJECT_CLASS (g_class);
793 SwfdecGcObjectClass *gc_class = SWFDEC_GC_OBJECT_CLASS (g_class);
794 SwfdecMovieClass *movie_class = SWFDEC_MOVIE_CLASS (g_class);
795 SwfdecActorClass *actor_class = SWFDEC_ACTOR_CLASS (g_class);
797 object_class->dispose = swfdec_sprite_movie_dispose;
798 object_class->constructor = swfdec_sprite_movie_constructor;
800 gc_class->mark = swfdec_sprite_movie_mark;
802 movie_class->init_movie = swfdec_sprite_movie_init_movie;
803 movie_class->finish_movie = swfdec_sprite_movie_finish_movie;
804 movie_class->property_get = swfdec_sprite_movie_property_get;
806 actor_class->iterate_start = swfdec_sprite_movie_iterate;
809 static void
810 swfdec_sprite_movie_init (SwfdecSpriteMovie * movie)
812 movie->playing = TRUE;
813 movie->frame = (guint) -1;
816 /* cute little hack */
817 extern void
818 swfdec_sprite_movie_clear (SwfdecAsContext *cx, SwfdecAsObject *object,
819 guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval);
821 * swfdec_sprite_movie_unload:
822 * @movie: a #SwfdecMovie
824 * Clears all contents from the given movie. This means deleting all
825 * variables and removing all children movie clips.
827 void
828 swfdec_sprite_movie_unload (SwfdecSpriteMovie *movie)
830 SwfdecMovie *mov;
831 SwfdecAsValue hack;
833 g_return_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie));
835 mov = SWFDEC_MOVIE (movie);
836 swfdec_sprite_movie_clear (swfdec_gc_object_get_context (movie),
837 SWFDEC_AS_OBJECT (movie), 0, NULL, &hack);
838 /* FIXME: destroy or unload? */
839 while (mov->list)
840 swfdec_movie_remove (mov->list->data);
841 swfdec_as_object_delete_all_variables (SWFDEC_AS_OBJECT (movie));
842 movie->frame = (guint) -1;
843 movie->n_frames = 0;
844 movie->next_action = 0;
845 movie->max_action = 0;
846 movie->sprite = NULL;
847 swfdec_movie_queue_update (SWFDEC_MOVIE (movie), SWFDEC_MOVIE_INVALID_EXTENTS);
851 * swfdec_sprite_movie_get_frames_loaded:
852 * @movie: a #SwfdecSpriteMovie
854 * Computes the number of loaded frames as used by the _framesloaded property
855 * or the WaitForFrame actions. If the @movie is fully loaded, this is the
856 * amount of total frames of the sprite it displays, or 0 if it has no sprite.
857 * If the movie is not fully loaded, it is the amount of frames that are
858 * completely loaded minus one. Welcome to the world of Flash.
860 * Returns: The number of loaded frames as reported by ActionScript.
863 swfdec_sprite_movie_get_frames_loaded (SwfdecSpriteMovie *movie)
865 SwfdecResource *resource;
866 SwfdecDecoder *dec;
868 g_return_val_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie), 0);
870 resource = swfdec_movie_get_own_resource (SWFDEC_MOVIE (movie));
871 if (resource == NULL) {
872 /* FIXME: can we set n_frames to 1 for movies without sprites instead? */
873 if (movie->sprite)
874 return movie->n_frames;
875 else
876 return 1;
878 dec = resource->decoder;
879 if (dec == NULL)
880 return -1;
881 if (dec->frames_loaded < dec->frames_total)
882 return dec->frames_loaded - 1;
883 return dec->frames_total;
887 swfdec_sprite_movie_get_frames_total (SwfdecSpriteMovie *movie)
889 SwfdecResource *resource;
890 SwfdecDecoder *dec;
892 g_return_val_if_fail (SWFDEC_IS_SPRITE_MOVIE (movie), 0);
894 resource = swfdec_movie_get_own_resource (SWFDEC_MOVIE (movie));
895 if (resource == NULL) {
896 /* FIXME: can we set n_frames to 1 for movies without sprites instead? */
897 if (movie->sprite)
898 return movie->n_frames;
899 else
900 return 1;
902 dec = resource->decoder;
903 if (dec == NULL)
904 return 0;
905 return dec->frames_total;