add blend mode tests
[swfdec.git] / swfdec / swfdec_as_interpret.c
blob525d824b3a5d57363a4f1229db4bf9cf0caa465c
1 /* Swfdec
2 * Copyright (C) 2007-2008 Benjamin Otte <otte@gnome.org>
3 * 2007 Pekka Lampila <pekka.lampila@iki.fi>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
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_as_interpret.h"
25 #include "swfdec_as_array.h"
26 #include "swfdec_as_context.h"
27 #include "swfdec_as_date.h"
28 #include "swfdec_as_frame_internal.h"
29 #include "swfdec_as_function.h"
30 #include "swfdec_as_internal.h"
31 #include "swfdec_as_script_function.h"
32 #include "swfdec_as_stack.h"
33 #include "swfdec_as_string.h"
34 #include "swfdec_as_strings.h"
35 #include "swfdec_as_super.h"
36 #include "swfdec_as_internal.h"
37 #include "swfdec_debug.h"
39 #include <errno.h>
40 #include <math.h>
41 #include <string.h>
42 #include "swfdec_decoder.h"
43 #include "swfdec_load_object.h"
44 #include "swfdec_movie.h"
45 #include "swfdec_player_internal.h"
46 #include "swfdec_sprite.h"
47 #include "swfdec_sprite_movie.h"
48 #include "swfdec_resource.h"
49 #include "swfdec_text_field_movie.h" // for typeof
51 /* Define this to get SWFDEC_WARN'd about missing properties of objects.
52 * This can be useful to find out about unimplemented native properties,
53 * but usually just causes a lot of spam. */
54 //#define SWFDEC_WARN_MISSING_PROPERTIES
56 /*** SUPPORT FUNCTIONS ***/
58 #define swfdec_action_has_register(cx, i) \
59 ((i) < (cx)->frame->n_registers)
61 /*** ALL THE ACTION IS HERE ***/
63 static void
64 swfdec_action_stop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
66 SwfdecMovie *target = swfdec_as_frame_get_target (cx->frame);
67 if (SWFDEC_IS_SPRITE_MOVIE (target))
68 SWFDEC_SPRITE_MOVIE (target)->playing = FALSE;
69 else
70 SWFDEC_ERROR ("no movie to stop");
73 static void
74 swfdec_action_play (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
76 SwfdecMovie *target = swfdec_as_frame_get_target (cx->frame);
77 if (SWFDEC_IS_SPRITE_MOVIE(target))
78 SWFDEC_SPRITE_MOVIE (target)->playing = TRUE;
79 else
80 SWFDEC_ERROR ("no movie to play");
83 static void
84 swfdec_action_next_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
86 SwfdecMovie *target = swfdec_as_frame_get_target (cx->frame);
87 if (SWFDEC_IS_SPRITE_MOVIE (target)) {
88 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (target);
89 if (movie->frame < movie->n_frames) {
90 swfdec_sprite_movie_goto (movie, movie->frame + 1);
91 movie->playing = FALSE;
92 } else {
93 SWFDEC_INFO ("can't execute nextFrame, already at last frame");
95 } else {
96 SWFDEC_ERROR ("no movie to nextFrame on");
100 static void
101 swfdec_action_previous_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
103 SwfdecMovie *target = swfdec_as_frame_get_target (cx->frame);
104 if (SWFDEC_IS_SPRITE_MOVIE (target)) {
105 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (target);
106 if (movie->frame > 1) {
107 swfdec_sprite_movie_goto (movie, movie->frame - 1);
108 movie->playing = FALSE;
109 } else {
110 SWFDEC_INFO ("can't execute previousFrame, already at first frame");
112 } else {
113 SWFDEC_ERROR ("no movie to previousFrame on");
117 static void
118 swfdec_action_goto_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
120 SwfdecMovie *target;
121 guint frame;
123 if (len != 2) {
124 SWFDEC_ERROR ("GotoFrame action length invalid (is %u, should be 2", len);
125 return;
127 frame = data[0] | (data[1] << 8);
128 target = swfdec_as_frame_get_target (cx->frame);
129 if (SWFDEC_IS_SPRITE_MOVIE (target)) {
130 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (target);
131 swfdec_sprite_movie_goto (movie, frame + 1);
132 movie->playing = FALSE;
133 } else {
134 SWFDEC_ERROR ("no movie to goto on");
138 static void
139 swfdec_action_goto_label (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
141 SwfdecMovie *target;
142 if (!memchr (data, 0, len)) {
143 SWFDEC_ERROR ("GotoLabel action does not specify a string");
144 return;
147 target = swfdec_as_frame_get_target (cx->frame);
148 if (SWFDEC_IS_SPRITE_MOVIE (target)) {
149 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (target);
150 int frame;
151 if (movie->sprite == NULL ||
152 (frame = swfdec_sprite_get_frame (movie->sprite, (const char *) data)) == -1)
153 return;
154 swfdec_sprite_movie_goto (movie, frame + 1);
155 movie->playing = FALSE;
156 } else {
157 SWFDEC_ERROR ("no movie to goto on");
161 /* returns: frame to go to or 0 on error */
162 static guint
163 swfdec_value_to_frame (SwfdecAsContext *cx, SwfdecSpriteMovie *movie, SwfdecAsValue *val)
165 int frame;
167 if (movie->sprite == NULL)
168 return 0;
169 if (SWFDEC_AS_VALUE_IS_STRING (*val)) {
170 const char *name = SWFDEC_AS_VALUE_GET_STRING (*val);
171 double d;
172 if (strchr (name, ':')) {
173 SWFDEC_ERROR ("FIXME: handle targets");
175 /* treat valid encoded numbers as numbers, otherwise assume it's a frame label */
176 d = swfdec_as_value_to_number (cx, *val);
177 if (isnan (d))
178 frame = swfdec_sprite_get_frame (movie->sprite, name) + 1;
179 else
180 frame = d;
181 } else if (SWFDEC_AS_VALUE_IS_NUMBER (*val)) {
182 frame = swfdec_as_value_to_integer (cx, *val);
183 } else {
184 SWFDEC_WARNING ("cannot convert value to frame number");
185 /* FIXME: how do we treat undefined etc? */
186 frame = 0;
188 return frame <= 0 ? 0 : frame;
191 static void
192 swfdec_action_goto_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
194 SwfdecBits bits;
195 guint bias;
196 gboolean play;
197 SwfdecAsValue *val;
198 SwfdecMovie *target;
200 swfdec_bits_init_data (&bits, data, len);
201 if (swfdec_bits_getbits (&bits, 6)) {
202 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
204 bias = swfdec_bits_getbit (&bits);
205 play = swfdec_bits_getbit (&bits);
206 if (bias) {
207 bias = swfdec_bits_get_u16 (&bits);
209 val = swfdec_as_stack_peek (cx, 1);
210 /* now set it */
211 target = swfdec_as_frame_get_target (cx->frame);
212 if (SWFDEC_IS_SPRITE_MOVIE (target)) {
213 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (target);
214 guint frame = swfdec_value_to_frame (cx, movie, val);
215 if (frame > 0) {
216 frame += bias;
217 frame = CLAMP (frame, 1, movie->n_frames);
218 swfdec_sprite_movie_goto (movie, frame);
219 movie->playing = play;
221 } else {
222 SWFDEC_ERROR ("no movie to GotoFrame2 on");
224 swfdec_as_stack_pop (cx);
227 static void
228 swfdec_script_skip_actions (SwfdecAsContext *cx, guint jump)
230 SwfdecScript *script = cx->frame->script;
231 const guint8 *pc = cx->frame->pc;
232 const guint8 *endpc = script->buffer->data + script->buffer->length;
234 /* jump instructions */
235 do {
236 if (pc >= endpc)
237 break;
238 if (*pc & 0x80) {
239 if (pc + 2 >= endpc)
240 break;
241 pc += 3 + (pc[1] | (pc[2] << 8));
242 } else {
243 pc++;
245 } while (jump-- > 0);
246 cx->frame->pc = pc;
249 static void
250 swfdec_action_wait_for_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
252 SwfdecSpriteMovie *movie;
253 SwfdecMovie *target;
254 int frame, loaded;
256 if (len < 1) {
257 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
258 return;
260 target = swfdec_as_frame_get_target (cx->frame);
261 if (!SWFDEC_IS_SPRITE_MOVIE (target)) {
262 SWFDEC_ERROR ("no movie for WaitForFrame");
263 return;
266 movie = SWFDEC_SPRITE_MOVIE (target);
267 frame = swfdec_value_to_frame (cx, movie, swfdec_as_stack_pop (cx));
268 loaded = swfdec_sprite_movie_get_frames_loaded (movie);
269 if (loaded < (int) movie->n_frames &&
270 loaded < frame - 1)
271 swfdec_script_skip_actions (cx, data[0]);
274 static void
275 swfdec_action_wait_for_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
277 SwfdecSpriteMovie *movie;
278 SwfdecMovie *target;
279 guint jump;
280 int frame, loaded;
282 if (len != 3) {
283 SWFDEC_ERROR ("WaitForFrame action length invalid (is %u, should be 3", len);
284 return;
286 target = swfdec_as_frame_get_target (cx->frame);
287 if (!SWFDEC_IS_SPRITE_MOVIE (target)) {
288 SWFDEC_ERROR ("no movie for WaitForFrame");
289 return;
292 movie = SWFDEC_SPRITE_MOVIE (target);
293 frame = data[0] | (data[1] << 8);
294 jump = data[2];
295 loaded = swfdec_sprite_movie_get_frames_loaded (movie);
296 if (loaded < (int) movie->n_frames &&
297 loaded < frame)
298 swfdec_script_skip_actions (cx, jump);
301 static void
302 swfdec_action_constant_pool (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
304 SwfdecConstantPool *pool;
305 SwfdecAsFrame *frame;
306 SwfdecBuffer *buffer;
308 frame = cx->frame;
309 /* FIXME: lots of hackery to get at the buffer */
310 buffer = frame->script->buffer;
311 buffer = swfdec_buffer_new_subbuffer (buffer, data - buffer->data, len);
312 pool = swfdec_constant_pool_new (cx, buffer, cx->version);
313 swfdec_buffer_unref (buffer);
314 if (pool == NULL)
315 return;
316 if (frame->constant_pool)
317 swfdec_constant_pool_unref (frame->constant_pool);
318 frame->constant_pool = pool;
321 static void
322 swfdec_action_push (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
324 SwfdecBits bits;
326 swfdec_bits_init_data (&bits, data, len);
327 while (swfdec_bits_left (&bits)) {
328 guint type = swfdec_bits_get_u8 (&bits);
329 SWFDEC_LOG ("push type %u", type);
330 swfdec_as_stack_ensure_free (cx, 1);
331 switch (type) {
332 case 0: /* string */
334 char *s = swfdec_bits_get_string (&bits, cx->version);
335 if (s == NULL) {
336 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
337 SWFDEC_AS_STR_EMPTY);
338 } else {
339 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
340 swfdec_as_context_give_string (cx, s));
342 break;
344 case 1: /* float */
345 *swfdec_as_stack_push (cx) = swfdec_as_value_from_number (cx,
346 swfdec_bits_get_float (&bits));
347 break;
348 case 2: /* null */
349 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
350 break;
351 case 3: /* undefined */
352 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
353 break;
354 case 4: /* register */
356 guint regnum = swfdec_bits_get_u8 (&bits);
357 if (!swfdec_action_has_register (cx, regnum)) {
358 SWFDEC_ERROR ("cannot Push register %u: not enough registers", regnum);
359 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
360 } else {
361 *swfdec_as_stack_push (cx) = cx->frame->registers[regnum];
363 break;
365 case 5: /* boolean */
366 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx),
367 swfdec_bits_get_u8 (&bits) ? TRUE : FALSE);
368 break;
369 case 6: /* double */
370 *swfdec_as_stack_push (cx) = swfdec_as_value_from_number (cx,
371 swfdec_bits_get_double (&bits));
372 break;
373 case 7: /* 32bit int */
374 *swfdec_as_stack_push (cx) = swfdec_as_value_from_integer (cx,
375 swfdec_bits_get_s32 (&bits));
376 break;
377 case 8: /* 8bit ConstantPool address */
378 case 9: /* 16bit ConstantPool address */
380 guint i = type == 8 ? swfdec_bits_get_u8 (&bits) : swfdec_bits_get_u16 (&bits);
381 SwfdecConstantPool *pool = cx->frame->constant_pool;
382 if (pool == NULL) {
383 SWFDEC_ERROR ("no constant pool to push from");
384 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
385 break;
387 if (i >= swfdec_constant_pool_size (pool)) {
388 SWFDEC_ERROR ("constant pool index %u too high - only %u elements",
389 i, swfdec_constant_pool_size (pool));
390 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
391 break;
393 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
394 swfdec_constant_pool_get (pool, i));
395 break;
397 default:
398 SWFDEC_ERROR ("Push: unknown type %u, skipping", type);
399 break;
404 /* NB: name must be GC'd */
405 static SwfdecAsObject *
406 super_special_movie_lookup_magic (SwfdecAsContext *cx, SwfdecAsObject *o, const char *name)
408 SwfdecAsValue val;
410 if (o == NULL) {
411 o = swfdec_as_frame_get_variable (cx, cx->frame, name, NULL);
412 if (o == NULL)
413 return NULL;
415 if (o->movie) {
416 SwfdecMovie *ret = swfdec_movie_get_by_name (SWFDEC_MOVIE (o->relay), name, TRUE);
417 if (ret)
418 return swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (ret));
420 if (!swfdec_as_object_get_variable (o, name, &val))
421 return NULL;
422 if (!SWFDEC_AS_VALUE_IS_COMPOSITE (val))
423 return NULL;
424 return SWFDEC_AS_VALUE_GET_COMPOSITE (val);
427 static SwfdecAsObject *
428 swfdec_action_get_movie_by_slash_path (SwfdecAsContext *cx, const char *path)
430 SwfdecMovie *movie;
431 SwfdecAsObject *o;
433 movie = swfdec_as_frame_get_target (cx->frame);
434 if (movie == NULL)
435 return NULL;
436 if (*path == '/') {
437 movie = swfdec_movie_get_root (movie);
438 path++;
440 o = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie));
441 while (*path) {
442 char *slash = strchr (path, '/');
443 const char *name;
444 if (slash) {
445 if (slash == path)
446 return NULL;
447 name = swfdec_as_context_give_string (cx, g_strndup (path, slash - path));
448 path = slash + 1;
449 } else {
450 name = swfdec_as_context_get_string (cx, path);
451 path += strlen (path);
453 o = super_special_movie_lookup_magic (cx, o, name);
454 if (o == NULL || !o->movie)
455 return NULL;
457 return o;
460 SwfdecAsObject *
461 swfdec_action_lookup_object (SwfdecAsContext *cx, SwfdecAsObject *o, const char *path, const char *end)
463 gboolean dot_allowed = TRUE;
464 const char *start;
466 if (path == end) {
467 if (o == NULL) {
468 SwfdecMovie *movie = swfdec_as_frame_get_target (cx->frame);
469 if (movie)
470 o = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie));
472 return o;
475 if (path[0] == '/') {
476 if (o == NULL) {
477 SwfdecMovie *movie = swfdec_as_frame_get_target (cx->frame);
478 if (movie) {
479 movie = swfdec_movie_get_root (movie);
480 o = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie));
481 } else {
482 return NULL;
485 path++;
486 dot_allowed = FALSE;
488 while (path < end) {
489 for (start = path; path < end; path++) {
490 if (dot_allowed && path[0] == '.') {
491 if (end - path >= 2 && path[1] == '.') {
492 dot_allowed = FALSE;
493 continue;
495 } else if (path[0] == ':') {
496 if (path[1] == '/')
497 continue;
498 if (path == start) {
499 start++;
500 continue;
502 } else if (path[0] == '/') {
503 dot_allowed = FALSE;
504 } else if (path - start < 127) {
505 continue;
508 break;
511 /* parse variable */
512 if (start[0] == '.' && start[1] == '.' && start + 2 == path) {
513 SwfdecMovie *movie;
514 /* ".." goes back to parent */
515 if (o == NULL) {
516 GSList *walk;
517 for (walk = cx->frame->scope_chain; walk; walk = walk->next) {
518 o = walk->data;
519 if (o->movie) {
520 movie = SWFDEC_MOVIE (o->relay);
521 break;
524 if (walk == NULL)
525 movie = swfdec_as_frame_get_target (cx->frame);
526 if (movie == NULL)
527 return NULL;
528 } else if (o->movie) {
529 movie = SWFDEC_MOVIE (o->relay);
530 } else {
531 return NULL;
533 movie = movie->parent;
534 if (movie == NULL)
535 return NULL;
536 o = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie));
537 } else {
538 o = super_special_movie_lookup_magic (cx, o,
539 swfdec_as_context_give_string (cx, g_strndup (start, path - start)));
540 if (o == NULL)
541 return NULL;
543 if (path - start < 127)
544 path++;
547 return o;
550 /* FIXME: this function belongs into swfdec_movie.c */
551 SwfdecMovie *
552 swfdec_player_get_movie_from_value (SwfdecPlayer *player, SwfdecAsValue *val)
554 SwfdecAsContext *cx;
555 const char *s;
557 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
559 cx = SWFDEC_AS_CONTEXT (player);
560 s = swfdec_as_value_to_string (cx, *val);
561 return swfdec_player_get_movie_from_string (player, s);
564 SwfdecMovie *
565 swfdec_player_get_movie_from_string (SwfdecPlayer *player, const char *s)
567 SwfdecAsObject *ret;
569 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
570 g_return_val_if_fail (s != NULL, NULL);
572 ret = swfdec_action_lookup_object (SWFDEC_AS_CONTEXT (player), NULL, s, s + strlen (s));
573 if (ret == NULL || !ret->movie) {
574 SWFDEC_WARNING ("\"%s\" does not reference a movie", s);
575 return NULL;
577 return SWFDEC_MOVIE (ret->relay);
581 * swfdec_action_get_movie_by_path:
582 * @cx: a #SwfdecAsContext
583 * @path: the path to look up
584 * @object: pointer that takes the object that was looked up. The object may be
585 * %NULL.
586 * @variable: pointer that takes variable part of the path. The variable will
587 * be either %NULL or a non-gc'ed variable name.
589 * Looks up a Flash4-compatible path using "/", ":" and "." style syntax.
591 * Returns: The #SwfdecMovie that was looked up or %NULL if the path does not
592 * specify a valid movie.
594 static gboolean
595 swfdec_action_get_movie_by_path (SwfdecAsContext *cx, const char *path,
596 SwfdecAsObject **object, const char **variable)
598 SwfdecAsObject *movie;
599 char *end, *s;
601 g_assert (path != NULL);
602 g_assert (object != NULL);
603 g_assert (variable != NULL);
604 g_assert (cx->frame != NULL);
606 /* find dot or colon */
607 end = strpbrk (path, ".:");
609 /* if no dot or colon, look up slash-path */
610 if (end == NULL) {
611 /* shortcut for the general case */
612 if (strchr (path, '/') != NULL) {
613 movie = swfdec_action_get_movie_by_slash_path (cx, path);
614 if (movie) {
615 *object = movie;
616 *variable = NULL;
617 return TRUE;
621 *object = NULL;
622 *variable = path;
623 return TRUE;
625 /* find last dot or colon */
626 while ((s = strpbrk (end + 1, ".:")) != NULL)
627 end = s;
629 /* variable to use is the part after the last dot or colon */
630 *variable = end + 1;
631 /* look up object for start of path */
632 if (path == end)
633 movie = NULL;
634 else
635 movie = swfdec_action_lookup_object (cx, NULL, path, end);
636 if (movie) {
637 *object = movie;
638 return TRUE;
639 } else {
640 *variable = NULL;
641 return FALSE;
645 static void
646 swfdec_action_get_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
648 SwfdecAsValue *val;
649 const char *s;
650 SwfdecAsObject *object;
652 val = swfdec_as_stack_peek (cx, 1);
653 s = swfdec_as_value_to_string (cx, *val);
654 if (swfdec_action_get_movie_by_path (cx, s, &object, &s)) {
655 if (object) {
656 if (s) {
657 swfdec_as_object_get_variable (object, swfdec_as_context_get_string (cx, s), val);
658 } else {
659 SWFDEC_AS_VALUE_SET_MOVIE (val, SWFDEC_MOVIE (object->relay));
661 } else {
662 swfdec_as_frame_get_variable (cx, cx->frame, swfdec_as_context_get_string (cx, s), val);
664 } else {
665 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
666 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
667 SWFDEC_WARNING ("no variable named %s", s);
668 #endif
672 static void
673 swfdec_action_set_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
675 const char *s, *rest;
676 SwfdecAsObject *object;
678 s = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 2));
679 if (swfdec_action_get_movie_by_path (cx, s, &object, &rest)) {
680 if (object && rest) {
681 swfdec_as_object_set_variable (object, swfdec_as_context_get_string (cx, rest),
682 swfdec_as_stack_peek (cx, 1));
683 } else {
684 if (object)
685 rest = s;
686 else
687 rest = swfdec_as_context_get_string (cx, rest);
688 swfdec_as_frame_set_variable (cx, cx->frame, rest,
689 swfdec_as_stack_peek (cx, 1), TRUE, FALSE);
692 swfdec_as_stack_pop_n (cx, 2);
695 static void
696 swfdec_action_get_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
698 SwfdecMovie *movie;
699 guint id;
701 id = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 1));
702 if (!SWFDEC_IS_PLAYER (cx)) {
703 SWFDEC_INFO ("tried using GetProperty in a non-SwfdecPlayer context");
704 movie = NULL;
705 } else {
706 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
707 swfdec_as_stack_peek (cx, 2));
709 if (movie == NULL) {
710 SWFDEC_ERROR ("calling GetProperty not on a movieclip object");
711 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
712 } else if (id > (cx->version > 4 ? 21 : 18)) {
713 SWFDEC_WARNING ("trying to GetProperty %u, doesn't exist", id);
714 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
715 } else {
716 *swfdec_as_stack_peek (cx, 2) = swfdec_movie_property_get (movie, id);
718 swfdec_as_stack_pop (cx);
721 static void
722 swfdec_action_set_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
724 SwfdecMovie *movie;
725 guint id;
727 id = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 2));
728 if (!SWFDEC_IS_PLAYER (cx)) {
729 SWFDEC_INFO ("tried using GetProperty in a non-SwfdecPlayer context");
730 movie = NULL;
731 } else {
732 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
733 swfdec_as_stack_peek (cx, 3));
735 if (movie == NULL) {
736 SWFDEC_ERROR ("calling GetProperty not on a movieclip object");
737 } else if (id > (cx->version > 4 ? 21 : 18)) {
738 SWFDEC_WARNING ("trying to SetProperty %u, doesn't exist", id);
739 } else {
740 swfdec_movie_property_set (movie, id, *swfdec_as_stack_peek (cx, 1));
742 swfdec_as_stack_pop_n (cx, 3);
745 static void
746 swfdec_action_get_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
748 SwfdecAsObject *object = swfdec_as_value_to_object (cx, *swfdec_as_stack_peek (cx, 2));
749 if (object) {
750 const char *name;
751 name = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
752 swfdec_as_object_get_variable (object, name, swfdec_as_stack_peek (cx, 2));
753 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
754 if (SWFDEC_AS_VALUE_IS_UNDEFINED (*swfdec_as_stack_peek (cx, 2))) {
755 SWFDEC_WARNING ("no variable named %s:%s",
756 object->relay ? G_OBJECT_TYPE_NAME (object->relay) : ":", name);
758 #endif
759 } else {
760 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
762 swfdec_as_stack_pop (cx);
765 static void
766 swfdec_action_set_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
768 const char *name = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 2));
769 if (SWFDEC_AS_VALUE_IS_COMPOSITE (*swfdec_as_stack_peek (cx, 3))) {
770 SwfdecAsObject *o = SWFDEC_AS_VALUE_GET_COMPOSITE (*swfdec_as_stack_peek (cx, 3));
771 if (o)
772 swfdec_as_object_set_variable (o, name, swfdec_as_stack_peek (cx, 1));
774 swfdec_as_stack_pop_n (cx, 3);
777 static void
778 swfdec_action_trace (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
780 SwfdecAsValue *val;
781 const char *s;
783 val = swfdec_as_stack_peek (cx, 1);
784 if (SWFDEC_AS_VALUE_IS_UNDEFINED (*val)) {
785 s = SWFDEC_AS_STR_undefined;
786 } else {
787 s = swfdec_as_value_to_string (cx, *val);
789 swfdec_as_stack_pop (cx);
790 g_signal_emit_by_name (cx, "trace", s);
793 /* stack looks like this: [ function, this, arg1, arg2, ... ] */
794 /* stack must be at least 2 elements big */
795 static gboolean
796 swfdec_action_call (SwfdecAsContext *cx, guint n_args, SwfdecAsObject *super)
798 SwfdecAsFunction *fun;
799 SwfdecAsObject *thisp;
801 if (!SWFDEC_AS_VALUE_IS_OBJECT (*swfdec_as_stack_peek (cx, 1)))
802 goto error;
803 fun = (SwfdecAsFunction *) (SWFDEC_AS_VALUE_GET_OBJECT (*swfdec_as_stack_peek (cx, 1))->relay);
804 if (!SWFDEC_IS_AS_FUNCTION (fun))
805 goto error;
806 if (!SWFDEC_AS_VALUE_IS_COMPOSITE (*swfdec_as_stack_peek (cx, 2))) {
807 thisp = NULL;
808 } else {
809 thisp = SWFDEC_AS_VALUE_GET_COMPOSITE (*swfdec_as_stack_peek (cx, 2));
811 swfdec_as_stack_pop_n (cx, 2);
812 /* sanitize argument count */
813 if (n_args >= swfdec_as_stack_get_size (cx))
814 n_args = swfdec_as_stack_get_size (cx);
815 if (super == NULL && SWFDEC_IS_AS_SUPER (fun)) {
816 SWFDEC_LOG ("replacing super object on frame");
817 super = swfdec_as_super_resolve_property (SWFDEC_AS_SUPER (fun), NULL);
819 swfdec_as_function_call_full (fun, thisp, FALSE, super, n_args, NULL, NULL);
820 return TRUE;
822 error:
823 n_args += 2;
824 if (n_args > swfdec_as_stack_get_size (cx))
825 n_args = swfdec_as_stack_get_size (cx);
826 swfdec_as_stack_pop_n (cx, n_args);
827 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
828 return FALSE;
831 static void
832 swfdec_action_call_function (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
834 SwfdecAsFrame *frame = cx->frame;
835 SwfdecAsObject *obj;
836 guint n_args;
837 const char *name;
838 SwfdecAsValue *fun, *thisp;
840 swfdec_as_stack_ensure_size (cx, 2);
841 n_args = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 2));
842 name = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
843 thisp = swfdec_as_stack_peek (cx, 2);
844 fun = swfdec_as_stack_peek (cx, 1);
845 obj = swfdec_as_frame_get_variable (cx, frame, name, fun);
846 if (obj) {
847 SWFDEC_AS_VALUE_SET_COMPOSITE (thisp, obj);
848 } else {
849 SWFDEC_AS_VALUE_SET_NULL (thisp);
850 SWFDEC_AS_VALUE_SET_UNDEFINED (fun);
852 if (!swfdec_action_call (cx, n_args, NULL)) {
853 SWFDEC_WARNING ("no function named %s", name);
857 static void
858 swfdec_action_call_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
860 SwfdecAsValue *val;
861 SwfdecAsObject *obj, *super;
862 SwfdecAsObject *pobj = NULL;
863 guint n_args;
864 const char *name;
866 swfdec_as_stack_ensure_size (cx, 3);
867 obj = swfdec_as_value_to_object (cx, *swfdec_as_stack_peek (cx, 2));
868 n_args = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 3));
869 val = swfdec_as_stack_peek (cx, 1);
870 if (obj) {
871 name = swfdec_as_value_to_string (cx, *val);
872 if (SWFDEC_AS_VALUE_IS_UNDEFINED (*val) ||
873 name == SWFDEC_AS_STR_EMPTY) {
874 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 3));
875 SWFDEC_AS_VALUE_SET_COMPOSITE (swfdec_as_stack_peek (cx, 2), obj);
876 name = "";
877 pobj = obj;
878 } else {
879 SWFDEC_AS_VALUE_SET_COMPOSITE (swfdec_as_stack_peek (cx, 3), obj);
880 swfdec_as_object_get_variable_and_flags (obj, name, swfdec_as_stack_peek (cx, 2), NULL, &pobj);
882 } else {
883 if (SWFDEC_AS_VALUE_IS_STRING (*val))
884 name = SWFDEC_AS_VALUE_GET_STRING (*val);
885 else
886 name = "???";
887 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_peek (cx, 3));
888 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
890 swfdec_as_stack_pop (cx);
891 /* setup super to point to the right prototype */
892 if (obj && SWFDEC_IS_AS_SUPER (obj->relay)) {
893 super = swfdec_as_super_resolve_property (SWFDEC_AS_SUPER (obj->relay), name);
894 } else if (cx->version > 6 && pobj != obj) {
895 super = pobj;
896 } else if (obj) {
897 super = obj->prototype;
898 } else {
899 super = NULL;
901 if (!swfdec_action_call (cx, n_args, super)) {
902 SWFDEC_WARNING ("no function named \"%s\" on object %s", name,
903 obj && obj->relay ? G_OBJECT_TYPE_NAME(obj->relay) : "unknown");
907 static void
908 swfdec_action_pop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
910 swfdec_as_stack_pop (cx);
913 static void
914 swfdec_action_binary (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
916 double l, r;
918 r = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 1));
919 l = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 2));
920 switch (action) {
921 case SWFDEC_AS_ACTION_ADD:
922 l = l + r;
923 break;
924 case SWFDEC_AS_ACTION_SUBTRACT:
925 l = l - r;
926 break;
927 case SWFDEC_AS_ACTION_MULTIPLY:
928 l = l * r;
929 break;
930 case SWFDEC_AS_ACTION_DIVIDE:
931 if (cx->version < 5) {
932 if (r == 0) {
933 swfdec_as_stack_pop (cx);
934 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), SWFDEC_AS_STR__ERROR_);
935 return;
938 if (r == 0) {
939 if (l > 0)
940 l = INFINITY;
941 else if (l < 0)
942 l = -INFINITY;
943 else
944 l = NAN;
945 } else {
946 l = l / r;
948 break;
949 default:
950 g_assert_not_reached ();
951 break;
953 swfdec_as_stack_pop (cx);
954 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_number (cx, l);
957 static void
958 swfdec_action_add2_to_primitive (SwfdecAsValue *value)
960 SwfdecAsObject *object;
961 const char *name;
963 if (!SWFDEC_AS_VALUE_IS_OBJECT (*value))
964 return;
965 object = SWFDEC_AS_VALUE_GET_OBJECT (*value);
967 if (SWFDEC_IS_AS_DATE (object->relay) && object->context->version > 5)
968 name = SWFDEC_AS_STR_toString;
969 else
970 name = SWFDEC_AS_STR_valueOf;
971 swfdec_as_object_call (object, name, 0, NULL, value);
974 static void
975 swfdec_action_add2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
977 SwfdecAsValue *rval, *lval, rtmp, ltmp;
979 rval = swfdec_as_stack_peek (cx, 1);
980 lval = swfdec_as_stack_peek (cx, 2);
981 rtmp = *rval;
982 ltmp = *lval;
983 swfdec_action_add2_to_primitive (&rtmp);
984 if (!SWFDEC_AS_VALUE_IS_OBJECT (rtmp))
985 rval = &rtmp;
986 swfdec_action_add2_to_primitive (&ltmp);
987 if (!SWFDEC_AS_VALUE_IS_OBJECT (ltmp))
988 lval = &ltmp;
990 if (SWFDEC_AS_VALUE_IS_STRING (*lval) || SWFDEC_AS_VALUE_IS_STRING (*rval)) {
991 const char *lstr, *rstr;
992 lstr = swfdec_as_value_to_string (cx, *lval);
993 rstr = swfdec_as_value_to_string (cx, *rval);
994 lstr = swfdec_as_str_concat (cx, lstr, rstr);
995 swfdec_as_stack_pop (cx);
996 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), lstr);
997 } else {
998 double d, d2;
999 d = swfdec_as_value_to_number (cx, *lval);
1000 d2 = swfdec_as_value_to_number (cx, *rval);
1001 d += d2;
1002 swfdec_as_stack_pop (cx);
1003 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_number (cx, d);
1007 static void
1008 swfdec_action_new_comparison (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1010 SwfdecAsValue *lval, *rval;
1011 double l, r;
1013 rval = swfdec_as_stack_peek (cx, 1);
1014 lval = swfdec_as_stack_peek (cx, 2);
1016 /* swap if we do a greater comparison */
1017 if (action == SWFDEC_AS_ACTION_GREATER) {
1018 SwfdecAsValue *tmp = lval;
1019 lval = rval;
1020 rval = tmp;
1022 /* comparison with object is always false */
1023 *lval = swfdec_as_value_to_primitive (*lval);
1024 if (SWFDEC_AS_VALUE_IS_OBJECT (*lval)) {
1025 swfdec_as_stack_pop (cx);
1026 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
1027 return;
1029 /* same for the rval */
1030 *rval = swfdec_as_value_to_primitive (*rval);
1031 if (SWFDEC_AS_VALUE_IS_OBJECT (*rval)) {
1032 swfdec_as_stack_pop (cx);
1033 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
1034 return;
1036 /* movieclips are not objects, but they evaluate to NaN, so we can handle them here */
1037 if (SWFDEC_AS_VALUE_IS_MOVIE (*rval) ||
1038 SWFDEC_AS_VALUE_IS_MOVIE (*lval)) {
1039 swfdec_as_stack_pop (cx);
1040 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1041 return;
1043 /* if both are strings, compare strings */
1044 if (SWFDEC_AS_VALUE_IS_STRING (*rval) &&
1045 SWFDEC_AS_VALUE_IS_STRING (*lval)) {
1046 const char *ls = SWFDEC_AS_VALUE_GET_STRING (*lval);
1047 const char *rs = SWFDEC_AS_VALUE_GET_STRING (*rval);
1048 int cmp;
1049 if (ls == SWFDEC_AS_STR_EMPTY) {
1050 cmp = rs == SWFDEC_AS_STR_EMPTY ? 0 : 1;
1051 } else if (rs == SWFDEC_AS_STR_EMPTY) {
1052 cmp = -1;
1053 } else {
1054 cmp = strcmp (ls, rs);
1056 swfdec_as_stack_pop (cx);
1057 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cmp < 0);
1058 return;
1060 /* convert to numbers and compare those */
1061 l = swfdec_as_value_to_number (cx, *lval);
1062 r = swfdec_as_value_to_number (cx, *rval);
1063 swfdec_as_stack_pop (cx);
1064 /* NaN results in undefined */
1065 if (isnan (l) || isnan (r)) {
1066 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1067 return;
1069 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), l < r);
1072 static void
1073 swfdec_action_not (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1075 if (cx->version <= 4) {
1076 double d = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 1));
1077 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_integer (cx, d == 0 ? 1 : 0);
1078 } else {
1079 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1),
1080 !swfdec_as_value_to_boolean (cx, *swfdec_as_stack_peek (cx, 1)));
1084 static void
1085 swfdec_action_jump (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1087 gint16 offset;
1089 if (len != 2) {
1090 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2)", len);
1091 return;
1093 offset = data[0] | (data[1] << 8);
1094 cx->frame->pc += 5 + (int) offset;
1097 static void
1098 swfdec_action_if (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1100 if (len != 2) {
1101 SWFDEC_ERROR ("If action length invalid (is %u, should be 2)", len);
1102 return;
1104 if (swfdec_as_value_to_boolean (cx, *swfdec_as_stack_peek (cx, 1))) {
1105 gint16 offset = data[0] | (data[1] << 8);
1106 cx->frame->pc += 5 + (int) offset;
1108 swfdec_as_stack_pop (cx);
1111 static void
1112 swfdec_action_decrement (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1114 SwfdecAsValue *val;
1116 val = swfdec_as_stack_peek (cx, 1);
1117 *val = swfdec_as_value_from_number (cx, swfdec_as_value_to_number (cx, *val) - 1);
1120 static void
1121 swfdec_action_increment (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1123 SwfdecAsValue *val;
1125 val = swfdec_as_stack_peek (cx, 1);
1126 *val = swfdec_as_value_from_number (cx, swfdec_as_value_to_number (cx, *val) + 1);
1129 static void
1130 swfdec_action_get_url (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1132 SwfdecBits bits;
1133 char *url, *t;
1134 const char *target;
1136 swfdec_bits_init_data (&bits, data, len);
1137 url = swfdec_bits_get_string (&bits, cx->version);
1138 t = swfdec_bits_get_string (&bits, cx->version);
1139 if (url == NULL || t == NULL) {
1140 SWFDEC_ERROR ("not enough data in GetURL");
1141 g_free (url);
1142 g_free (t);
1143 return;
1145 target = swfdec_as_context_give_string (cx, t);
1146 if (swfdec_bits_left (&bits)) {
1147 SWFDEC_WARNING ("leftover bytes in GetURL action");
1149 if (!SWFDEC_IS_PLAYER (cx)) {
1150 SWFDEC_ERROR ("GetURL without a SwfdecPlayer");
1151 } else {
1152 swfdec_resource_load (SWFDEC_PLAYER (cx), target, url, NULL);
1154 g_free (url);
1157 static void
1158 swfdec_as_interpret_load_variables_on_finish (SwfdecPlayer *player,
1159 const SwfdecAsValue *val, const char *text)
1161 SwfdecMovie *movie = SWFDEC_AS_VALUE_GET_MOVIE (*val);
1163 if (movie == NULL)
1164 return;
1166 if (text != NULL)
1167 swfdec_as_object_decode (swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie)), text);
1169 // only call onData for sprite movies
1170 swfdec_actor_queue_script (SWFDEC_ACTOR (movie), SWFDEC_EVENT_DATA);
1173 static gboolean
1174 swfdec_as_interpret_encode_variables_foreach (SwfdecAsObject *object,
1175 const char *variable, SwfdecAsValue *value, guint flags, gpointer data)
1177 SwfdecAsContext *context;
1178 GString *variables = data;
1179 char *escaped;
1181 context = object->context;
1182 // FIXME: check propflags?
1184 if (variables->len > 0)
1185 g_string_append_c (variables, '&');
1187 escaped = swfdec_as_string_escape (context, variable);
1188 g_string_append (variables, escaped);
1189 g_free (escaped);
1191 g_string_append_c (variables, '=');
1193 escaped = swfdec_as_string_escape (context,
1194 swfdec_as_value_to_string (context, *value));
1195 g_string_append (variables, escaped);
1196 g_free (escaped);
1198 return TRUE;
1201 static char *
1202 swfdec_as_interpret_encode_variables (SwfdecAsObject *object)
1204 GString *variables = g_string_new ("");
1206 SWFDEC_FIXME ("Encoding variables for getURL2 shouldn't include child movies");
1207 swfdec_as_object_foreach (object,
1208 swfdec_as_interpret_encode_variables_foreach, variables);
1210 return g_string_free (variables, FALSE);
1213 static void
1214 swfdec_action_get_url2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1216 const char *target, *url;
1217 guint method, internal, variables;
1218 SwfdecBuffer *buffer;
1219 SwfdecAsValue val;
1221 if (len != 1) {
1222 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
1223 return;
1226 method = data[0] & 3;
1227 if (method == 3) {
1228 SWFDEC_ERROR ("GetURL method 3 invalid");
1229 method = 0;
1231 internal = data[0] & 64;
1232 variables = data[0] & 128;
1234 url = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 2));
1235 buffer = NULL;
1237 if (method == 1 || method == 2) {
1238 SwfdecMovie *movie;
1239 char *text;
1241 movie = swfdec_as_frame_get_target (cx->frame);
1242 if (movie == NULL) {
1243 SWFDEC_FIXME ("no target, what do we encode now?");
1244 return;
1246 text = swfdec_as_interpret_encode_variables (swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (movie)));
1247 if (method == 1) {
1248 url = swfdec_as_context_give_string (cx, g_strjoin (NULL, url,
1249 strchr (url, '?') == NULL ? "?" : "&", text, NULL));
1250 } else {
1251 // don't send the nul-byte
1252 buffer = swfdec_buffer_new_for_data (g_memdup (text, strlen (text)),
1253 strlen (text));
1255 g_free (text);
1258 if (!SWFDEC_IS_PLAYER (cx)) {
1259 SWFDEC_ERROR ("GetURL2 action requires a SwfdecPlayer");
1260 } else if (variables) {
1261 SwfdecMovie *movie;
1263 target = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
1264 movie = swfdec_player_get_movie_from_string (SWFDEC_PLAYER (cx), target);
1265 if (movie != NULL) {
1266 SWFDEC_AS_VALUE_SET_MOVIE (&val, movie);
1267 swfdec_load_object_create (SWFDEC_PLAYER (cx), &val, url, buffer, 0,
1268 NULL, NULL, NULL, swfdec_as_interpret_load_variables_on_finish);
1270 } else if (internal) {
1271 swfdec_resource_load_movie (SWFDEC_PLAYER (cx), swfdec_as_stack_peek (cx, 1),
1272 url, NULL, NULL);
1273 } else {
1274 target = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
1275 swfdec_resource_load (SWFDEC_PLAYER (cx), target, url, buffer);
1278 swfdec_as_stack_pop_n (cx, 2);
1281 static void
1282 swfdec_action_string_add (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1284 const char *lval, *rval;
1286 rval = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
1287 lval = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 2));
1288 lval = swfdec_as_str_concat (cx, lval, rval);
1289 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 2), lval);
1290 swfdec_as_stack_pop (cx);
1293 static void
1294 swfdec_action_push_duplicate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1296 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
1298 *swfdec_as_stack_push (cx) = *val;
1301 static void
1302 swfdec_action_random_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1304 gint32 max;
1305 SwfdecAsValue *val;
1307 val = swfdec_as_stack_peek (cx, 1);
1308 max = swfdec_as_value_to_integer (cx, *val);
1310 if (max <= 0)
1311 *val = swfdec_as_value_from_number (cx, 0);
1312 else
1313 *val = swfdec_as_value_from_number (cx, g_rand_int_range (cx->rand, 0, max));
1316 static void
1317 swfdec_action_old_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1319 double l, r;
1320 gboolean cond;
1322 l = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 2));
1323 r = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 1));
1324 switch (action) {
1325 case SWFDEC_AS_ACTION_EQUALS:
1326 cond = l == r;
1327 break;
1328 case SWFDEC_AS_ACTION_LESS:
1329 cond = l < r;
1330 break;
1331 default:
1332 g_assert_not_reached ();
1333 return;
1335 swfdec_as_stack_pop (cx);
1336 if (cx->version < 5) {
1337 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_integer (cx, cond ? 1 : 0);
1338 } else {
1339 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1343 static void
1344 swfdec_action_string_extract (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1346 int start, n, left;
1347 const char *s;
1349 n = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 1));
1350 start = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 2));
1351 s = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 3));
1352 swfdec_as_stack_pop_n (cx, 2);
1353 left = g_utf8_strlen (s, -1);
1354 if (start > left) {
1355 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), SWFDEC_AS_STR_EMPTY);
1356 return;
1357 } else if (start < 2) {
1358 start = 0;
1359 } else {
1360 start--;
1362 left -= start;
1363 if (n < 0 || n > left)
1364 n = left;
1366 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1),
1367 swfdec_as_str_sub (cx, s, start, n));
1370 static void
1371 swfdec_action_string_length (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1373 const char *s;
1374 SwfdecAsValue *v;
1376 v = swfdec_as_stack_peek (cx, 1);
1377 s = swfdec_as_value_to_string (cx, *v);
1378 *v = swfdec_as_value_from_integer (cx, g_utf8_strlen (s, -1));
1381 static void
1382 swfdec_action_string_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1384 const char *l, *r;
1385 gboolean cond;
1387 r = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
1388 l = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 2));
1389 switch (action) {
1390 case SWFDEC_AS_ACTION_STRING_EQUALS:
1391 cond = l == r;
1392 break;
1393 case SWFDEC_AS_ACTION_STRING_LESS:
1394 cond = strcmp (l, r) < 0;
1395 break;
1396 case SWFDEC_AS_ACTION_STRING_GREATER:
1397 cond = strcmp (l, r) > 0;
1398 break;
1399 default:
1400 cond = FALSE;
1401 g_assert_not_reached ();
1402 break;
1404 swfdec_as_stack_pop (cx);
1405 if (cx->version < 5) {
1406 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_integer (cx, cond ? 1 : 0);
1407 } else {
1408 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1412 static void
1413 swfdec_action_equals2_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1415 SwfdecAsValue *rval, *lval;
1416 SwfdecAsValue rtmp, ltmp;
1417 SwfdecAsValueType ltype, rtype;
1418 double l, r;
1419 gboolean cond;
1421 rval = swfdec_as_stack_peek (cx, 1);
1422 lval = swfdec_as_stack_peek (cx, 2);
1423 rtmp = swfdec_as_value_to_primitive (*rval);
1424 ltmp = swfdec_as_value_to_primitive (*lval);
1425 ltype = SWFDEC_AS_VALUE_GET_TYPE (ltmp);
1426 rtype = SWFDEC_AS_VALUE_GET_TYPE (rtmp);
1428 if (SWFDEC_AS_VALUE_IS_COMPOSITE (ltmp) && SWFDEC_AS_VALUE_IS_COMPOSITE (rtmp)) {
1429 /* get movies compared */
1430 if (ltype == SWFDEC_AS_TYPE_MOVIE) {
1431 if (rtype == SWFDEC_AS_TYPE_MOVIE) {
1432 rval = &rtmp;
1433 } else {
1434 *rval = swfdec_as_value_to_primitive (*rval);
1436 cond = SWFDEC_AS_VALUE_IS_MOVIE (*rval) &&
1437 SWFDEC_AS_VALUE_GET_MOVIE (ltmp) == SWFDEC_AS_VALUE_GET_MOVIE (*rval);
1438 goto out;
1440 if (rtype == SWFDEC_AS_TYPE_MOVIE) {
1441 *lval = swfdec_as_value_to_primitive (*lval);
1442 cond = SWFDEC_AS_VALUE_IS_MOVIE (*lval) &&
1443 SWFDEC_AS_VALUE_GET_MOVIE (rtmp) == SWFDEC_AS_VALUE_GET_MOVIE (*lval);
1444 goto out;
1447 cond = SWFDEC_AS_VALUE_GET_OBJECT (*lval) == SWFDEC_AS_VALUE_GET_OBJECT (*rval);
1448 goto out;
1451 /* compare strings */
1452 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1453 cond = SWFDEC_AS_VALUE_GET_STRING (ltmp) == SWFDEC_AS_VALUE_GET_STRING (rtmp);
1454 goto out;
1457 /* convert to numbers */
1458 if (SWFDEC_AS_VALUE_IS_OBJECT (ltmp)) {
1459 l = swfdec_as_value_to_number (cx, *lval);
1460 } else {
1461 l = swfdec_as_value_to_number (cx, ltmp);
1463 if (SWFDEC_AS_VALUE_IS_OBJECT (rtmp)) {
1464 r = swfdec_as_value_to_number (cx, *rval);
1465 } else {
1466 r = swfdec_as_value_to_number (cx, rtmp);
1469 /* get rid of undefined and null */
1470 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1471 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1472 goto out;
1473 } else if (cond) {
1474 cond = FALSE;
1475 goto out;
1478 /* else compare as numbers */
1479 if (isnan (l) && isnan (r)) {
1480 cond = (ltype == SWFDEC_AS_TYPE_NUMBER && ltmp == rtmp);
1481 } else {
1482 cond = l == r;
1485 out:
1486 swfdec_as_stack_pop (cx);
1487 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1490 static void
1491 swfdec_action_equals2_6 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1493 SwfdecAsValue *rval, *lval;
1494 SwfdecAsValueType ltype, rtype;
1495 double l, r;
1496 gboolean cond;
1498 rval = swfdec_as_stack_peek (cx, 1);
1499 lval = swfdec_as_stack_peek (cx, 2);
1500 /* check objects before anything else */
1501 if (SWFDEC_AS_VALUE_IS_OBJECT (*lval) && SWFDEC_AS_VALUE_IS_OBJECT (*rval)) {
1502 cond = SWFDEC_AS_VALUE_GET_OBJECT (*lval) == SWFDEC_AS_VALUE_GET_OBJECT (*rval);
1503 goto out;
1505 *lval = swfdec_as_value_to_primitive (*lval);
1506 *rval = swfdec_as_value_to_primitive (*rval);
1508 /* check if we have equal movieclips */
1509 if (SWFDEC_AS_VALUE_IS_MOVIE (*lval)) {
1510 cond = SWFDEC_AS_VALUE_IS_MOVIE (*rval) &&
1511 SWFDEC_AS_VALUE_GET_MOVIE (*lval) == SWFDEC_AS_VALUE_GET_MOVIE (*rval);
1512 goto out;
1515 /* now all composites compare false */
1516 if (SWFDEC_AS_VALUE_IS_COMPOSITE (*lval) ||
1517 SWFDEC_AS_VALUE_IS_COMPOSITE (*rval)) {
1518 cond = FALSE;
1519 goto out;
1522 ltype = SWFDEC_AS_VALUE_GET_TYPE (*lval);
1523 rtype = SWFDEC_AS_VALUE_GET_TYPE (*rval);
1525 /* get rid of undefined and null */
1526 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1527 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1528 goto out;
1529 } else if (cond) {
1530 cond = FALSE;
1531 goto out;
1534 /* compare strings */
1535 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1536 cond = SWFDEC_AS_VALUE_GET_STRING (*lval) == SWFDEC_AS_VALUE_GET_STRING (*rval);
1537 goto out;
1540 /* else compare as numbers */
1541 l = swfdec_as_value_to_number (cx, *lval);
1542 r = swfdec_as_value_to_number (cx, *rval);
1544 if (isnan (l) && isnan (r)) {
1545 cond = (ltype == SWFDEC_AS_TYPE_NUMBER && *lval == *rval);
1546 } else {
1547 cond = l == r;
1550 out:
1551 swfdec_as_stack_pop (cx);
1552 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1555 static void
1556 swfdec_action_equals2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1558 if (cx->version <= 5) {
1559 swfdec_action_equals2_5 (cx, action, data, len);
1560 } else {
1561 swfdec_action_equals2_6 (cx, action, data, len);
1565 static void
1566 swfdec_action_strict_equals (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1568 SwfdecAsValue *rval, *lval;
1569 gboolean cond;
1571 rval = swfdec_as_stack_peek (cx, 1);
1572 lval = swfdec_as_stack_peek (cx, 2);
1574 if (SWFDEC_AS_VALUE_GET_TYPE (*rval) != SWFDEC_AS_VALUE_GET_TYPE (*lval)) {
1575 cond = FALSE;
1576 } else {
1577 switch (SWFDEC_AS_VALUE_GET_TYPE (*rval)) {
1578 case SWFDEC_AS_TYPE_UNDEFINED:
1579 case SWFDEC_AS_TYPE_NULL:
1580 cond = TRUE;
1581 break;
1582 case SWFDEC_AS_TYPE_BOOLEAN:
1583 cond = SWFDEC_AS_VALUE_GET_BOOLEAN (*rval) == SWFDEC_AS_VALUE_GET_BOOLEAN (*lval);
1584 break;
1585 case SWFDEC_AS_TYPE_NUMBER:
1587 double l, r;
1588 r = SWFDEC_AS_VALUE_GET_NUMBER (*rval);
1589 l = SWFDEC_AS_VALUE_GET_NUMBER (*lval);
1590 cond = (l == r) || (isnan (l) && isnan (r));
1592 break;
1593 case SWFDEC_AS_TYPE_STRING:
1594 cond = SWFDEC_AS_VALUE_GET_STRING (*rval) == SWFDEC_AS_VALUE_GET_STRING (*lval);
1595 break;
1596 case SWFDEC_AS_TYPE_OBJECT:
1597 cond = SWFDEC_AS_VALUE_GET_OBJECT (*lval) == SWFDEC_AS_VALUE_GET_OBJECT (*rval);
1598 break;
1599 case SWFDEC_AS_TYPE_MOVIE:
1600 cond = SWFDEC_AS_VALUE_GET_MOVIE (*lval) == SWFDEC_AS_VALUE_GET_MOVIE (*rval);
1601 break;
1602 case SWFDEC_AS_TYPE_INT:
1603 default:
1604 g_assert_not_reached ();
1605 cond = FALSE;
1609 swfdec_as_stack_pop (cx);
1610 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1613 static void
1614 swfdec_action_do_set_target (SwfdecAsContext *cx, const char *target, const char *end)
1616 swfdec_as_frame_set_target (cx->frame, NULL);
1618 if (target != end) {
1619 SwfdecAsObject *o = swfdec_action_lookup_object (cx, NULL, target, end);
1620 if (o == NULL) {
1621 SWFDEC_WARNING ("target \"%s\" is not an object", target);
1622 } else if (!o->movie) {
1623 SWFDEC_FIXME ("target \"%s\" is not a movie, something weird is supposed to happen now", target);
1624 } else {
1625 swfdec_as_frame_set_target (cx->frame, SWFDEC_MOVIE (o->relay));
1630 static void
1631 swfdec_action_set_target (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1633 char *end;
1635 end = memchr (data, 0, len);
1636 if (end == NULL) {
1637 SWFDEC_ERROR ("SetTarget action does not specify a string");
1638 return;
1640 swfdec_action_do_set_target (cx, (const char *) data, end);
1643 static void
1644 swfdec_action_set_target2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1646 const char *s;
1648 s = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
1649 swfdec_action_do_set_target (cx, s, s + strlen (s));
1650 swfdec_as_stack_pop (cx);
1653 static void
1654 swfdec_action_start_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1656 SwfdecRect rect, *rectp = NULL;
1657 SwfdecMovie *movie;
1658 gboolean center;
1659 guint stack_size = 3;
1661 swfdec_as_stack_ensure_size (cx, 3);
1662 center = swfdec_as_value_to_boolean (cx, *swfdec_as_stack_peek (cx, 2));
1663 if (swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 3))) {
1664 swfdec_as_stack_ensure_size (cx, 7);
1665 rect.x0 = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 7));
1666 rect.y0 = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 6));
1667 rect.x1 = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 5));
1668 rect.y1 = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 4));
1669 swfdec_rect_scale (&rect, &rect, SWFDEC_TWIPS_SCALE_FACTOR);
1670 stack_size = 7;
1671 rectp = &rect;
1673 if (!SWFDEC_IS_PLAYER (cx)) {
1674 SWFDEC_ERROR ("called startDrag on a non-SwfdecPlayer");
1675 } else {
1676 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx), swfdec_as_stack_peek (cx, 1));
1677 if (SWFDEC_IS_ACTOR (movie)) {
1678 swfdec_player_set_drag_movie (SWFDEC_PLAYER (cx), SWFDEC_ACTOR (movie), center, rectp);
1679 } else {
1680 SWFDEC_ERROR ("startDrag on something not an Actor");
1683 swfdec_as_stack_pop_n (cx, stack_size);
1686 static void
1687 swfdec_action_end_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1689 if (SWFDEC_IS_PLAYER (cx)) {
1690 swfdec_player_set_drag_movie (SWFDEC_PLAYER (cx), NULL, FALSE, NULL);
1691 } else {
1692 SWFDEC_WARNING ("can't end a drag on non-players");
1696 static void
1697 swfdec_action_stop_sounds (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1699 if (SWFDEC_IS_PLAYER (cx)) {
1700 swfdec_player_stop_all_sounds (SWFDEC_PLAYER (cx));
1704 static void
1705 swfdec_action_new_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1707 SwfdecAsValue *constructor;
1708 SwfdecAsFunction *fun;
1709 guint n_args;
1711 swfdec_as_stack_ensure_size (cx, 2);
1712 swfdec_action_get_variable (cx, action, data, len);
1713 constructor = swfdec_as_stack_peek (cx, 1);
1714 n_args = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 2));
1715 n_args = MIN (swfdec_as_stack_get_size (cx) - 2, n_args);
1716 if (!SWFDEC_AS_VALUE_IS_OBJECT (*constructor) ||
1717 !SWFDEC_IS_AS_FUNCTION (fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (*constructor)->relay)) {
1718 SWFDEC_WARNING ("not a constructor");
1719 goto fail;
1722 swfdec_as_stack_pop_n (cx, 2);
1723 swfdec_as_object_create (fun, n_args, NULL, NULL);
1724 return;
1726 fail:
1727 swfdec_as_stack_pop_n (cx, n_args + 1);
1728 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1731 static void
1732 swfdec_action_new_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1734 SwfdecAsValue *constructor;
1735 SwfdecAsFunction *fun;
1736 guint n_args;
1737 const char *name;
1739 swfdec_as_stack_ensure_size (cx, 3);
1740 name = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
1742 constructor = swfdec_as_stack_peek (cx, 2);
1743 n_args = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 3));
1744 n_args = MIN (swfdec_as_stack_get_size (cx) - 3, n_args);
1745 if (name == SWFDEC_AS_STR_EMPTY ||
1746 SWFDEC_AS_VALUE_IS_UNDEFINED (*swfdec_as_stack_peek (cx, 1))) {
1747 } else {
1748 if (!SWFDEC_AS_VALUE_IS_COMPOSITE (*constructor)) {
1749 SWFDEC_WARNING ("NewMethod called without an object to get variable %s from", name);
1750 goto fail;
1752 swfdec_as_object_get_variable (SWFDEC_AS_VALUE_GET_COMPOSITE (*constructor),
1753 name, constructor);
1755 if (!SWFDEC_AS_VALUE_IS_OBJECT (*constructor) ||
1756 !SWFDEC_IS_AS_FUNCTION (fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (*constructor)->relay)) {
1757 SWFDEC_WARNING ("%s is not a constructor", name);
1758 goto fail;
1761 swfdec_as_stack_pop_n (cx, 3);
1762 swfdec_as_object_create (fun, n_args, NULL, NULL);
1763 return;
1765 fail:
1766 swfdec_as_stack_pop_n (cx, n_args + 2);
1767 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1770 static void
1771 swfdec_action_init_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1773 SwfdecAsObject *object;
1774 guint i, n_args, size;
1776 n_args = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 1));
1777 swfdec_as_stack_pop (cx);
1778 if (n_args * 2 > swfdec_as_stack_get_size (cx)) {
1779 size = swfdec_as_stack_get_size (cx);
1780 SWFDEC_FIXME ("InitObject action with too small stack, help!");
1781 n_args = size / 2;
1782 size &= 1;
1783 } else {
1784 size = 0;
1787 object = swfdec_as_object_new (cx, NULL);
1788 swfdec_as_object_set_constructor_by_name (object, SWFDEC_AS_STR_Object, NULL);
1789 for (i = 0; i < n_args; i++) {
1790 const char *s = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 2));
1791 swfdec_as_object_set_variable (object, s, swfdec_as_stack_peek (cx, 1));
1792 swfdec_as_stack_pop_n (cx, 2);
1794 swfdec_as_stack_pop_n (cx, size);
1795 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), object);
1798 static void
1799 swfdec_action_init_array (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1801 int i, n;
1802 SwfdecAsObject *array;
1804 swfdec_as_stack_ensure_size (cx, 1);
1805 n = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 1));
1806 swfdec_as_stack_pop (cx);
1807 array = swfdec_as_array_new (cx);
1808 /* NB: we can't increase the stack here, as the number can easily be MAXINT */
1809 for (i = 0; i < n && swfdec_as_stack_get_size (cx) > 0; i++) {
1810 swfdec_as_stack_ensure_size (cx, 1);
1811 swfdec_as_array_push (array, swfdec_as_stack_pop (cx));
1813 if (i != n) {
1814 SwfdecAsValue val;
1815 val = swfdec_as_value_from_integer (cx, n);
1816 swfdec_as_object_set_variable (array, SWFDEC_AS_STR_length, &val);
1818 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), array);
1821 static void
1822 swfdec_action_define_function (SwfdecAsContext *cx, guint action,
1823 const guint8 *data, guint len)
1825 char *function_name;
1826 const char *name = NULL;
1827 guint i, n_args, size, n_registers;
1828 SwfdecBits bits;
1829 SwfdecBuffer *buffer;
1830 SwfdecAsFunction *fun;
1831 SwfdecAsFrame *frame;
1832 SwfdecScript *script;
1833 guint flags = 0;
1834 SwfdecScriptArgument *args;
1835 gboolean v2 = (action == 0x8e);
1837 frame = cx->frame;
1838 swfdec_bits_init_data (&bits, data, len);
1839 function_name = swfdec_bits_get_string (&bits, cx->version);
1840 if (function_name == NULL) {
1841 SWFDEC_ERROR ("could not parse function name");
1842 return;
1844 n_args = swfdec_bits_get_u16 (&bits);
1845 if (v2) {
1846 n_registers = swfdec_bits_get_u8 (&bits);
1847 if (n_registers == 0)
1848 n_registers = 4;
1849 flags = swfdec_bits_get_u16 (&bits);
1850 } else {
1851 n_registers = 5;
1853 if (n_args) {
1854 args = g_new0 (SwfdecScriptArgument, n_args);
1855 for (i = 0; i < n_args && swfdec_bits_left (&bits); i++) {
1856 if (v2) {
1857 args[i].preload = swfdec_bits_get_u8 (&bits);
1858 if (args[i].preload && args[i].preload >= n_registers) {
1859 SWFDEC_ERROR ("argument %u cannot be preloaded into register %u out of %u",
1860 i, args[i].preload, n_registers);
1861 /* FIXME: figure out correct error handling here */
1862 args[i].preload = 0;
1865 args[i].name = swfdec_bits_get_string (&bits, cx->version);
1866 if (args[i].name == NULL || args[i].name == '\0') {
1867 SWFDEC_ERROR ("empty argument name not allowed");
1868 g_free (args);
1869 return;
1871 /* FIXME: check duplicate arguments */
1873 } else {
1874 args = NULL;
1876 size = swfdec_bits_get_u16 (&bits);
1877 /* check the script can be created */
1878 if (frame->script->buffer->data + frame->script->buffer->length < frame->pc + 3 + len + size) {
1879 SWFDEC_ERROR ("size of function is too big");
1880 g_free (args);
1881 g_free (function_name);
1882 return;
1884 /* create the script */
1885 buffer = swfdec_buffer_new_subbuffer (frame->script->buffer,
1886 frame->pc + 3 + len - frame->script->buffer->data, size);
1887 swfdec_bits_init (&bits, buffer);
1888 if (*function_name) {
1889 name = function_name;
1890 } else if (swfdec_as_stack_get_size (cx) > 0) {
1891 /* This is kind of a hack that uses a feature of the Adobe compiler:
1892 * foo = function () {} is compiled as these actions:
1893 * Push "foo", DefineFunction, SetVariable/SetMember
1894 * With this knowledge we can inspect the topmost stack member, since
1895 * it will contain the name this function will soon be assigned to.
1897 if (SWFDEC_AS_VALUE_IS_STRING (*swfdec_as_stack_peek (cx, 1)))
1898 name = SWFDEC_AS_VALUE_GET_STRING (*swfdec_as_stack_peek (cx, 1));
1900 if (name == NULL)
1901 name = "unnamed_function";
1902 script = swfdec_script_new_from_bits (&bits, name, cx->version);
1903 swfdec_buffer_unref (buffer);
1904 if (script == NULL) {
1905 SWFDEC_ERROR ("failed to create script");
1906 g_free (args);
1907 g_free (function_name);
1908 return;
1910 if (frame->constant_pool)
1911 script->constant_pool = swfdec_buffer_ref (swfdec_constant_pool_get_buffer (frame->constant_pool));
1912 script->flags = flags;
1913 script->n_registers = n_registers;
1914 script->n_arguments = n_args;
1915 script->arguments = args;
1916 /* see function-scope tests */
1917 if (cx->version > 5) {
1918 /* FIXME: or original target? */
1919 fun = swfdec_as_script_function_new (cx, frame->original_target, frame->scope_chain, script);
1920 } else {
1921 fun = swfdec_as_script_function_new (cx, frame->original_target, NULL, script);
1923 /* This is a hack that should only trigger for functions defined in the init scripts.
1924 * It is supposed to ensure that those functions inherit their target when being
1925 * called instead of when being defined */
1926 if (!SWFDEC_IS_MOVIE (frame->original_target))
1927 SWFDEC_AS_SCRIPT_FUNCTION (fun)->target = NULL;
1928 /* attach the function */
1929 if (*function_name == '\0') {
1930 swfdec_as_stack_ensure_free (cx, 1);
1931 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx),
1932 swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (fun)));
1933 } else {
1934 SwfdecAsValue funval;
1935 SwfdecMovie *target = frame->original_target;
1936 if (target) {
1937 name = swfdec_as_context_get_string (cx, function_name);
1938 SWFDEC_AS_VALUE_SET_OBJECT (&funval, swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (fun)));
1939 swfdec_as_object_set_variable (swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (target)),
1940 name, &funval);
1944 /* update current context */
1945 frame->pc += 3 + len + size;
1946 g_free (function_name);
1949 static void
1950 swfdec_action_bitwise (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1952 int a, b;
1954 a = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 1));
1955 b = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 2));
1957 switch (action) {
1958 case 0x60:
1959 a = (int) (a & b);
1960 break;
1961 case 0x61:
1962 a = (int) (a | b);
1963 break;
1964 case 0x62:
1965 a = (int) (a ^ b);
1966 break;
1967 default:
1968 g_assert_not_reached ();
1969 break;
1972 swfdec_as_stack_pop (cx);
1973 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_integer (cx, a);
1976 static void
1977 swfdec_action_shift (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1979 int amount, value;
1981 amount = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 1));
1982 amount &= 31;
1983 value = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 2));
1985 switch (action) {
1986 case 0x63:
1987 value = value << amount;
1988 break;
1989 case 0x64:
1990 value = ((gint) value) >> amount;
1991 break;
1992 case 0x65:
1993 value = ((guint) value) >> amount;
1994 break;
1995 default:
1996 g_assert_not_reached ();
1999 swfdec_as_stack_pop (cx);
2000 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_integer (cx, value);
2003 static void
2004 swfdec_action_to_integer (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2006 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2008 *val = swfdec_as_value_from_integer (cx, swfdec_as_value_to_integer (cx, *val));
2011 static void
2012 swfdec_action_target_path (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2014 SwfdecAsValue *val;
2015 char *s;
2017 val = swfdec_as_stack_peek (cx, 1);
2019 if (!SWFDEC_AS_VALUE_IS_MOVIE (*val)) {
2020 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
2021 return;
2023 s = swfdec_movie_get_path (SWFDEC_AS_VALUE_GET_MOVIE (*val), TRUE);
2024 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_give_string (cx, s));
2027 static void
2028 swfdec_action_define_local (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2030 const char *name;
2032 name = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 2));
2033 swfdec_as_frame_set_variable (cx, cx->frame, name, swfdec_as_stack_peek (cx, 1),
2034 TRUE, TRUE);
2035 swfdec_as_stack_pop_n (cx, 2);
2038 static void
2039 swfdec_action_define_local2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2041 SwfdecAsValue val = { 0, };
2042 const char *name;
2044 name = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
2045 swfdec_as_frame_set_variable (cx, cx->frame, name, &val, FALSE, TRUE);
2046 swfdec_as_stack_pop (cx);
2049 static void
2050 swfdec_action_end (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2052 swfdec_as_context_return (cx, NULL);
2055 static void
2056 swfdec_action_return (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2058 swfdec_as_context_return (cx, swfdec_as_stack_pop (cx));
2061 static void
2062 swfdec_action_delete (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2064 SwfdecAsValue *val;
2065 const char *name;
2066 gboolean success = FALSE;
2068 name = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1));
2069 val = swfdec_as_stack_peek (cx, 2);
2070 if (SWFDEC_AS_VALUE_IS_COMPOSITE (*val)) {
2071 SwfdecAsObject *o = SWFDEC_AS_VALUE_GET_COMPOSITE (*val);
2072 if (o)
2073 success = swfdec_as_object_delete_variable (o, name) == SWFDEC_AS_DELETE_DELETED;
2075 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
2076 swfdec_as_stack_pop_n (cx, 1);
2079 static void
2080 swfdec_action_delete2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2082 SwfdecAsValue *val;
2083 const char *name;
2084 gboolean success = FALSE;
2086 val = swfdec_as_stack_peek (cx, 1);
2087 name = swfdec_as_value_to_string (cx, *val);
2088 success = swfdec_as_frame_delete_variable (cx, cx->frame, name) == SWFDEC_AS_DELETE_DELETED;
2089 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
2092 static void
2093 swfdec_action_store_register (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2095 if (len != 1) {
2096 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
2097 return;
2099 if (!swfdec_action_has_register (cx, *data)) {
2100 SWFDEC_ERROR ("Cannot store into register %u, not enough registers", (guint) *data);
2101 return;
2103 cx->frame->registers[*data] = *swfdec_as_stack_peek (cx, 1);
2106 static void
2107 swfdec_action_modulo (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2109 double x, y;
2111 y = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 1));
2112 x = swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 2));
2113 /* yay, we're portable! */
2114 if (y == 0.0) {
2115 x = NAN;
2116 } else {
2117 errno = 0;
2118 x = fmod (x, y);
2119 if (errno != 0) {
2120 SWFDEC_FIXME ("errno set after fmod");
2123 swfdec_as_stack_pop (cx);
2124 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_number (cx, x);
2127 static void
2128 swfdec_action_swap (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2130 swfdec_as_stack_swap (cx, 1, 2);
2133 static void
2134 swfdec_action_to_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2136 *swfdec_as_stack_peek (cx, 1) = swfdec_as_value_from_number (cx,
2137 swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 1)));
2140 static void
2141 swfdec_action_to_string (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2143 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1),
2144 swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 1)));
2147 static void
2148 swfdec_action_type_of (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2150 SwfdecAsValue val;
2151 const char *type;
2153 val = *swfdec_as_stack_pop (cx);
2154 switch (SWFDEC_AS_VALUE_GET_TYPE (val)) {
2155 case SWFDEC_AS_TYPE_NUMBER:
2156 type = SWFDEC_AS_STR_number;
2157 break;
2158 case SWFDEC_AS_TYPE_BOOLEAN:
2159 type = SWFDEC_AS_STR_boolean;
2160 break;
2161 case SWFDEC_AS_TYPE_STRING:
2162 type = SWFDEC_AS_STR_string;
2163 break;
2164 case SWFDEC_AS_TYPE_UNDEFINED:
2165 type = SWFDEC_AS_STR_undefined;
2166 break;
2167 case SWFDEC_AS_TYPE_NULL:
2168 type = SWFDEC_AS_STR_null;
2169 break;
2170 case SWFDEC_AS_TYPE_OBJECT:
2172 SwfdecAsObject *obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
2173 if (SWFDEC_IS_AS_FUNCTION (obj->relay)) {
2174 type = SWFDEC_AS_STR_function;
2175 } else {
2176 type = SWFDEC_AS_STR_object;
2179 break;
2180 case SWFDEC_AS_TYPE_MOVIE:
2182 SwfdecMovie *movie = SWFDEC_AS_VALUE_GET_MOVIE (val);
2183 if (SWFDEC_IS_TEXT_FIELD_MOVIE (movie) &&
2184 movie->state == SWFDEC_MOVIE_STATE_RUNNING) {
2185 type = SWFDEC_AS_STR_object;
2186 } else {
2187 type = SWFDEC_AS_STR_movieclip;
2190 break;
2191 case SWFDEC_AS_TYPE_INT:
2192 default:
2193 g_assert_not_reached ();
2194 type = SWFDEC_AS_STR_EMPTY;
2195 break;
2197 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx), type);
2200 static void
2201 swfdec_action_get_time (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2203 GTimeVal tv;
2204 double diff;
2206 swfdec_as_context_get_time (cx, &tv);
2207 /* we assume here that swfdec_as_context_get_time always returns a tv > start_time */
2208 diff = tv.tv_sec - cx->start_time.tv_sec;
2209 diff *= 1000;
2210 diff += (tv.tv_usec - cx->start_time.tv_usec) / 1000;
2212 *swfdec_as_stack_push (cx) = swfdec_as_value_from_number (cx, diff);
2215 static gboolean
2216 swfdec_action_is_instance_of (SwfdecAsObject *object,
2217 SwfdecAsObject *constructor)
2219 SwfdecAsValue val;
2220 SwfdecAsObject *class, *prototype;
2221 GSList *iter;
2223 g_return_val_if_fail (object != NULL, FALSE);
2224 g_return_val_if_fail (constructor != NULL, FALSE);
2226 // FIXME: propflag tests are wrong, and we shouldn't get __proto__.prototype
2227 swfdec_as_object_get_variable (constructor, SWFDEC_AS_STR_prototype, &val);
2228 if (!SWFDEC_AS_VALUE_IS_OBJECT (val))
2229 return FALSE;
2230 prototype = SWFDEC_AS_VALUE_GET_OBJECT (val);
2232 class = object;
2233 while ((class = swfdec_as_object_get_prototype (class)) != NULL) {
2234 if (class == prototype)
2235 return TRUE;
2236 for (iter = class->interfaces; iter != NULL; iter = iter->next) {
2237 if (iter->data == prototype)
2238 return TRUE;
2242 return FALSE;
2245 static void
2246 swfdec_action_instance_of (SwfdecAsContext *cx, guint action,
2247 const guint8 *data, guint len)
2249 SwfdecAsValue *val;
2250 SwfdecAsObject *object, *constructor;
2252 val = swfdec_as_stack_pop (cx);
2253 if (SWFDEC_AS_VALUE_IS_OBJECT (*val)) {
2254 constructor = SWFDEC_AS_VALUE_GET_OBJECT (*val);
2255 } else {
2256 constructor = NULL;
2259 val = swfdec_as_stack_pop (cx);
2260 if (SWFDEC_AS_VALUE_IS_COMPOSITE (*val)) {
2261 object = SWFDEC_AS_VALUE_GET_COMPOSITE (*val);
2262 } else {
2263 object = NULL;
2267 if (object == NULL || constructor == NULL) {
2268 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx), FALSE);
2269 return;
2272 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx),
2273 swfdec_action_is_instance_of (object, constructor));
2276 static void
2277 swfdec_action_cast (SwfdecAsContext *cx, guint action, const guint8 *data,
2278 guint len)
2280 SwfdecAsValue *val;
2281 SwfdecAsObject *object, *constructor;
2283 val = swfdec_as_stack_pop (cx);
2284 if (SWFDEC_AS_VALUE_IS_COMPOSITE (*val)) {
2285 object = SWFDEC_AS_VALUE_GET_COMPOSITE (*val);
2286 } else {
2287 object = NULL;
2290 val = swfdec_as_stack_pop (cx);
2291 if (SWFDEC_AS_VALUE_IS_OBJECT (*val)) {
2292 constructor = SWFDEC_AS_VALUE_GET_OBJECT (*val);
2293 } else {
2294 constructor = NULL;
2297 if (object == NULL || constructor == NULL) {
2298 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
2299 return;
2302 if (swfdec_action_is_instance_of (object, constructor)) {
2303 SWFDEC_AS_VALUE_SET_COMPOSITE (swfdec_as_stack_push (cx), object);
2304 } else {
2305 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
2309 static void
2310 swfdec_action_implements (SwfdecAsContext *cx, guint action,
2311 const guint8 *data, guint len)
2313 SwfdecAsValue *val, *argv;
2314 SwfdecAsObject *object, *proto;
2315 int argc, i;
2317 swfdec_as_stack_ensure_size (cx, 2);
2319 val = swfdec_as_stack_pop (cx);
2320 if (SWFDEC_AS_VALUE_IS_COMPOSITE (*val)) {
2321 object = SWFDEC_AS_VALUE_GET_COMPOSITE (*val);
2322 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_prototype, val);
2323 if (SWFDEC_AS_VALUE_IS_OBJECT (*val)) {
2324 proto = SWFDEC_AS_VALUE_GET_OBJECT (*val);
2325 } else {
2326 proto = NULL;
2328 } else {
2329 object = NULL;
2330 proto = NULL;
2333 val = swfdec_as_stack_pop (cx);
2334 argc = swfdec_as_value_to_integer (cx, *val);
2336 if (argc > 0) {
2337 swfdec_as_stack_ensure_size (cx, argc);
2338 argv = swfdec_as_stack_pop_n (cx, argc);
2339 } else {
2340 argv = NULL;
2343 if (proto == NULL)
2344 return;
2346 for (i = 0; i < argc; i++) {
2347 swfdec_as_value_get_variable (cx, &argv[i], SWFDEC_AS_STR_prototype, val);
2348 if (!SWFDEC_AS_VALUE_IS_OBJECT (*val))
2349 continue;
2350 proto->interfaces =
2351 g_slist_prepend (proto->interfaces, SWFDEC_AS_VALUE_GET_OBJECT (*val));
2355 static void
2356 swfdec_action_extends (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2358 SwfdecAsValue *superclass, *subclass, proto;
2359 SwfdecAsObject *prototype;
2360 SwfdecAsObject *super;
2362 superclass = swfdec_as_stack_peek (cx, 1);
2363 subclass = swfdec_as_stack_peek (cx, 2);
2364 if (!SWFDEC_AS_VALUE_IS_OBJECT (*superclass) ||
2365 !SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (*superclass)->relay)) {
2366 SWFDEC_ERROR ("superclass is not a function");
2367 goto fail;
2369 if (!SWFDEC_AS_VALUE_IS_COMPOSITE (*subclass)) {
2370 SWFDEC_ERROR ("subclass is not an object");
2371 goto fail;
2373 super = SWFDEC_AS_VALUE_GET_OBJECT (*superclass);
2374 prototype = swfdec_as_object_new_empty (cx);
2375 swfdec_as_object_get_variable (super, SWFDEC_AS_STR_prototype, &proto);
2376 swfdec_as_object_set_variable_and_flags (prototype, SWFDEC_AS_STR___proto__, &proto,
2377 SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
2378 swfdec_as_object_set_variable_and_flags (prototype, SWFDEC_AS_STR___constructor__,
2379 superclass, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_VERSION_6_UP);
2380 SWFDEC_AS_VALUE_SET_OBJECT (&proto, prototype);
2381 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_COMPOSITE (*subclass),
2382 SWFDEC_AS_STR_prototype, &proto);
2383 fail:
2384 swfdec_as_stack_pop_n (cx, 2);
2387 static void
2388 swfdec_action_do_enumerate (SwfdecAsContext *cx, SwfdecAsObject *object)
2390 guint i;
2391 GSList *walk, *list;
2393 list = swfdec_as_object_enumerate (object);
2394 i = 0;
2395 for (walk = list; walk; walk = walk->next) {
2396 /* 8 is an arbitrary value */
2397 if (i % 8 == 0) {
2398 swfdec_as_stack_ensure_free (cx, 8);
2399 i = 0;
2401 i++;
2402 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx), walk->data);
2404 g_slist_free (list);
2407 static void
2408 swfdec_action_enumerate2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2410 SwfdecAsValue *val;
2411 SwfdecAsObject *obj;
2413 val = swfdec_as_stack_peek (cx, 1);
2414 if (!SWFDEC_AS_VALUE_IS_COMPOSITE (*val) ||
2415 (obj = SWFDEC_AS_VALUE_GET_COMPOSITE (*val)) == NULL) {
2416 SWFDEC_WARNING ("Enumerate called without an object");
2417 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
2418 return;
2420 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
2421 swfdec_action_do_enumerate (cx, obj);
2424 static void
2425 swfdec_action_enumerate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2427 /* FIXME: make this proper functions */
2428 swfdec_action_get_variable (cx, action, data, len);
2429 swfdec_action_enumerate2 (cx, action, data, len);
2432 static void
2433 swfdec_action_logical (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2435 gboolean l, r;
2437 if (cx->version <= 4) {
2438 l = (swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 1)) != 0);
2439 // don't call second parameter if not necessary
2440 if ((action == SWFDEC_AS_ACTION_AND && !l) ||
2441 (action != SWFDEC_AS_ACTION_AND && l)) {
2442 r = (swfdec_as_value_to_number (cx, *swfdec_as_stack_peek (cx, 2)) != 0);
2443 } else {
2444 r = FALSE;
2446 } else {
2447 l = swfdec_as_value_to_boolean (cx, *swfdec_as_stack_peek (cx, 1));
2448 r = swfdec_as_value_to_boolean (cx, *swfdec_as_stack_peek (cx, 2));
2451 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 2),
2452 (action == SWFDEC_AS_ACTION_AND) ? (l && r) : (l || r));
2453 swfdec_as_stack_pop (cx);
2456 static void
2457 swfdec_action_char_to_ascii (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2459 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2460 const char *s = swfdec_as_value_to_string (cx, *val);
2462 if (cx->version <= 5) {
2463 char *ascii = g_convert (s, -1, "LATIN1", "UTF-8", NULL, NULL, NULL);
2465 if (ascii == NULL) {
2466 /* This can happen if a Flash 5 movie gets loaded into a Flash 7 movie */
2467 SWFDEC_FIXME ("Someone threw unconvertible text %s at Flash <= 5", s);
2468 *val = swfdec_as_value_from_integer (cx, 0); /* FIXME: what to return??? */
2469 } else {
2470 *val = swfdec_as_value_from_integer (cx, (guchar) ascii[0]);
2471 g_free (ascii);
2473 } else {
2474 gunichar *uni = g_utf8_to_ucs4_fast (s, -1, NULL);
2476 if (uni == NULL) {
2477 /* This should never happen, everything is valid UTF-8 in here */
2478 g_warning ("conversion of character %s failed", s);
2479 *val = swfdec_as_value_from_integer (cx, 0);
2480 } else {
2481 *val = swfdec_as_value_from_integer (cx, uni[0]);
2482 g_free (uni);
2487 static void
2488 swfdec_action_ascii_to_char (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2490 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2492 if (cx->version <= 5) {
2493 char s[3];
2494 char *utf8;
2495 guint i;
2497 if (action == SWFDEC_AS_ACTION_ASCII_TO_CHAR) {
2498 s[0] = ((guint) swfdec_as_value_to_integer (cx, *val)) % 256;
2499 s[1] = 0;
2500 } else {
2501 g_assert (action == SWFDEC_AS_ACTION_MB_ASCII_TO_CHAR);
2503 i = ((guint) swfdec_as_value_to_integer (cx, *val));
2504 if (i > 255) {
2505 s[0] = i / 256;
2506 s[1] = i % 256;
2507 s[2] = 0;
2508 } else {
2509 s[0] = i;
2510 s[1] = 0;
2514 utf8 = g_convert (s, -1, "UTF-8", "LATIN1", NULL, NULL, NULL);
2515 if (utf8 == NULL) {
2516 g_warning ("conversion of character %u failed", (guint) s[0]);
2517 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2518 } else {
2519 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, utf8));
2520 g_free (utf8);
2522 } else {
2523 char *s;
2524 gunichar c = ((guint) swfdec_as_value_to_integer (cx, *val)) % 65536;
2526 s = g_ucs4_to_utf8 (&c, 1, NULL, NULL, NULL);
2527 if (s == NULL) {
2528 g_warning ("conversion of character %u failed", (guint) c);
2529 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2530 } else {
2531 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, s));
2532 g_free (s);
2537 static void
2538 swfdec_action_throw (SwfdecAsContext *cx, guint action, const guint8 *data,
2539 guint len)
2541 swfdec_as_context_throw (cx, swfdec_as_stack_pop (cx));
2544 typedef struct {
2545 const guint8 * catch_start;
2546 const guint8 * finally_start;
2547 guint catch_size;
2548 guint finally_size;
2550 gboolean use_register;
2551 union {
2552 guint register_number;
2553 char * variable_name;
2556 SwfdecAsObject * scope_object;
2557 } TryData;
2559 static void
2560 swfdec_action_try_data_free (gpointer data)
2562 TryData *try_data = data;
2564 g_return_if_fail (try_data != NULL);
2566 if (!try_data->use_register)
2567 g_free (try_data->variable_name);
2568 g_free (try_data);
2571 static void
2572 swfdec_action_try_end_finally (SwfdecAsContext *cx, SwfdecAsFrame *frame, gpointer data)
2574 SwfdecAsValue *exception_value = data;
2577 // finally has ended and we had exception stored, throw it
2578 if (!cx->exception && cx->frame == frame)
2579 swfdec_as_context_throw (cx, exception_value);
2581 g_free (data);
2584 static void
2585 swfdec_action_try_end_catch (SwfdecAsContext *cx, SwfdecAsFrame *frame, gpointer data)
2587 TryData *try_data = data;
2588 SwfdecAsValue *exception_value, val;
2590 g_return_if_fail (try_data != NULL);
2592 if (try_data->scope_object) {
2593 g_assert (frame->scope_chain->data == try_data->scope_object);
2594 frame->scope_chain =
2595 g_slist_delete_link (frame->scope_chain, frame->scope_chain);
2596 try_data->scope_object = NULL;
2599 if (try_data->finally_start && swfdec_as_context_catch (cx, &val))
2601 // we got an exception while in catch block:
2602 // create new block for finally to pass on the exception
2603 // jump to that block
2605 exception_value = g_malloc (sizeof (SwfdecAsValue));
2606 *exception_value = val;
2608 // FIXME: the exception value is not marked while finally block runs
2609 swfdec_as_frame_push_block (frame, try_data->finally_start,
2610 try_data->finally_start + try_data->finally_size,
2611 swfdec_action_try_end_finally, exception_value);
2612 frame->pc = try_data->finally_start;
2615 swfdec_action_try_data_free (try_data);
2618 static void
2619 swfdec_action_try_end_try (SwfdecAsContext *cx, SwfdecAsFrame *frame, gpointer data)
2621 TryData *try_data = data;
2622 SwfdecAsValue val;
2624 g_return_if_fail (try_data != NULL);
2626 // if we don't have a catch block, we handle try block exactly like it was
2627 // catch block
2628 if (!try_data->catch_start) {
2629 swfdec_action_try_end_catch (cx, frame, try_data);
2630 return;
2633 if (swfdec_as_context_catch (cx, &val))
2635 // we got an exception while in try block:
2636 // set the exception variable
2637 // add new block for catch and jump to it
2638 try_data->scope_object = swfdec_as_object_new_empty (cx);
2639 frame->scope_chain = g_slist_prepend (frame->scope_chain,
2640 try_data->scope_object);
2642 swfdec_as_frame_push_block (frame, try_data->catch_start,
2643 try_data->catch_start + try_data->catch_size,
2644 swfdec_action_try_end_catch, try_data);
2645 frame->pc = try_data->catch_start;
2647 if (try_data->use_register)
2649 if (try_data->register_number < frame->n_registers) {
2650 frame->registers[try_data->register_number] = val;
2651 } else {
2652 SWFDEC_ERROR ("cannot set Error to register %u: not enough registers",
2653 try_data->register_number);
2656 else
2658 swfdec_as_object_set_variable (try_data->scope_object,
2659 swfdec_as_context_get_string (cx, try_data->variable_name), &val);
2662 else
2664 swfdec_action_try_data_free (try_data);
2668 static void
2669 swfdec_action_try (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2671 SwfdecBits bits;
2672 TryData *try_data;
2673 guint try_size;
2674 gboolean use_finally, use_catch;
2676 if (len < 8) {
2677 SWFDEC_ERROR ("With action requires a length of at least 8, but got %u",
2678 len);
2679 return;
2682 try_data = g_malloc0 (sizeof (TryData));
2684 swfdec_bits_init_data (&bits, data, len);
2686 swfdec_bits_getbits (&bits, 5); // reserved
2687 try_data->use_register = swfdec_bits_getbit (&bits);
2688 use_finally = swfdec_bits_getbit (&bits);
2689 use_catch = swfdec_bits_getbit (&bits);
2691 try_size = swfdec_bits_get_u16 (&bits);
2692 try_data->catch_size = swfdec_bits_get_u16 (&bits);
2693 try_data->finally_size = swfdec_bits_get_u16 (&bits);
2694 if (use_catch)
2695 try_data->catch_start = data + len + try_size;
2696 if (use_finally) {
2697 try_data->finally_start = data + len + try_size +
2698 (use_catch ? try_data->catch_size : 0);
2701 if (try_data->use_register) {
2702 try_data->register_number = swfdec_bits_get_u8 (&bits);
2703 } else {
2704 try_data->variable_name =
2705 swfdec_bits_get_string (&bits, cx->version);
2708 if (swfdec_bits_left (&bits)) {
2709 SWFDEC_WARNING ("leftover bytes in Try action");
2712 if (try_data->catch_start || try_data->finally_start) {
2713 swfdec_as_frame_push_block (cx->frame, data + len, data + len + try_size,
2714 swfdec_action_try_end_try, try_data);
2715 } else {
2716 SWFDEC_WARNING ("Try with neither catch nor finally block");
2717 swfdec_action_try_data_free (try_data);
2721 static void
2722 swfdec_action_pop_with (SwfdecAsContext *cx, SwfdecAsFrame *frame, gpointer with_object)
2724 g_assert (frame->scope_chain->data == with_object);
2725 frame->scope_chain = g_slist_delete_link (frame->scope_chain, frame->scope_chain);
2728 static void
2729 swfdec_action_with (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2731 SwfdecAsObject *object;
2732 guint offset;
2734 if (len != 2) {
2735 SWFDEC_ERROR ("With action requires a length of 2, but got %u", len);
2736 swfdec_as_stack_pop (cx);
2737 return;
2739 offset = data[0] | (data[1] << 8);
2740 object = swfdec_as_value_to_object (cx, *swfdec_as_stack_peek (cx, 1));
2741 if (object == NULL) {
2742 SWFDEC_INFO ("With called without an object, skipping");
2743 cx->frame->pc = (guint8 *) data + len + offset;
2744 } else {
2745 cx->frame->scope_chain = g_slist_prepend (cx->frame->scope_chain, object);
2746 swfdec_as_frame_push_block (cx->frame, data + len, data + len + offset,
2747 swfdec_action_pop_with, object);
2749 swfdec_as_stack_pop (cx);
2752 static void
2753 swfdec_action_remove_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2755 SwfdecMovie *target = swfdec_as_frame_get_target (cx->frame);
2756 if (target == NULL) {
2757 SWFDEC_FIXME ("target is not a movie in RemoveSprite");
2758 } else if (!SWFDEC_IS_PLAYER (cx)) {
2759 SWFDEC_INFO ("tried using RemoveSprite in a non-SwfdecPlayer context");
2760 } else {
2761 SwfdecMovie *movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
2762 swfdec_as_stack_peek (cx, 1));
2763 if (movie && swfdec_depth_classify (movie->depth) == SWFDEC_DEPTH_CLASS_DYNAMIC) {
2764 SWFDEC_LOG ("removing clip %s", movie->name);
2765 swfdec_movie_remove (movie);
2766 } else {
2767 SWFDEC_INFO ("cannot remove movie");
2770 swfdec_as_stack_pop (cx);
2773 static void
2774 swfdec_action_clone_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2776 SwfdecMovie *target = swfdec_as_frame_get_target (cx->frame);
2777 SwfdecMovie *movie, *new_movie;
2778 const char *new_name;
2779 int depth;
2781 depth = swfdec_as_value_to_integer (cx, *swfdec_as_stack_peek (cx, 1)) - 16384;
2782 new_name = swfdec_as_value_to_string (cx, *swfdec_as_stack_peek (cx, 2));
2783 if (target == NULL) {
2784 SWFDEC_FIXME ("target is not a movie in CloneSprite");
2785 } else if (!SWFDEC_IS_PLAYER (cx)) {
2786 SWFDEC_INFO ("tried using CloneSprite in a non-SwfdecPlayer context");
2787 } else {
2788 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
2789 swfdec_as_stack_peek (cx, 3));
2790 if (movie == NULL) {
2791 SWFDEC_ERROR ("Object is not an SwfdecMovie object");
2792 swfdec_as_stack_pop_n (cx, 3);
2793 return;
2795 new_movie = swfdec_movie_duplicate (movie, new_name, depth);
2796 if (new_movie) {
2797 SWFDEC_LOG ("duplicated %s as %s to depth %u", movie->name, new_movie->name, new_movie->depth);
2800 swfdec_as_stack_pop_n (cx, 3);
2803 /*** BIG FUNCTION TABLE ***/
2805 const SwfdecActionSpec swfdec_as_actions[256] = {
2806 /* version 1 */
2807 [SWFDEC_AS_ACTION_END] = { "End", 0, 0, swfdec_action_end, 1 },
2808 [SWFDEC_AS_ACTION_NEXT_FRAME] = { "NextFrame", 0, 0, swfdec_action_next_frame, 1 },
2809 [SWFDEC_AS_ACTION_PREVIOUS_FRAME] = { "PreviousFrame", 0, 0, swfdec_action_previous_frame, 1 },
2810 [SWFDEC_AS_ACTION_PLAY] = { "Play", 0, 0, swfdec_action_play, 1 },
2811 [SWFDEC_AS_ACTION_STOP] = { "Stop", 0, 0, swfdec_action_stop, 1 },
2812 [SWFDEC_AS_ACTION_TOGGLE_QUALITY] = { "ToggleQuality", -1, -1, NULL, 1 },
2813 /* version 2 */
2814 [SWFDEC_AS_ACTION_STOP_SOUNDS] = { "StopSounds", 0, 0, swfdec_action_stop_sounds, 2 },
2815 /* version 4 */
2816 [SWFDEC_AS_ACTION_ADD] = { "Add", 2, 1, swfdec_action_binary, 4 },
2817 [SWFDEC_AS_ACTION_SUBTRACT] = { "Subtract", 2, 1, swfdec_action_binary, 4 },
2818 [SWFDEC_AS_ACTION_MULTIPLY] = { "Multiply", 2, 1, swfdec_action_binary, 4 },
2819 [SWFDEC_AS_ACTION_DIVIDE] = { "Divide", 2, 1, swfdec_action_binary, 4 },
2820 [SWFDEC_AS_ACTION_EQUALS] = { "Equals", 2, 1, swfdec_action_old_compare, 4 },
2821 [SWFDEC_AS_ACTION_LESS] = { "Less", 2, 1, swfdec_action_old_compare, 4 },
2822 [SWFDEC_AS_ACTION_AND] = { "And", 2, 1, swfdec_action_logical, 4 },
2823 [SWFDEC_AS_ACTION_OR] = { "Or", 2, 1, swfdec_action_logical, 4 },
2824 [SWFDEC_AS_ACTION_NOT] = { "Not", 1, 1, swfdec_action_not, 4 },
2825 [SWFDEC_AS_ACTION_STRING_EQUALS] = { "StringEquals", 2, 1, swfdec_action_string_compare, 4 },
2826 [SWFDEC_AS_ACTION_STRING_LENGTH] = { "StringLength", 1, 1, swfdec_action_string_length, 4 },
2827 [SWFDEC_AS_ACTION_STRING_EXTRACT] = { "StringExtract", 3, 1, swfdec_action_string_extract, 4 },
2828 [SWFDEC_AS_ACTION_POP] = { "Pop", 1, 0, swfdec_action_pop, 4 },
2829 [SWFDEC_AS_ACTION_TO_INTEGER] = { "ToInteger", 1, 1, swfdec_action_to_integer, 4 },
2830 [SWFDEC_AS_ACTION_GET_VARIABLE] = { "GetVariable", 1, 1, swfdec_action_get_variable, 4 },
2831 [SWFDEC_AS_ACTION_SET_VARIABLE] = { "SetVariable", 2, 0, swfdec_action_set_variable, 4 },
2832 /* version 3 */
2833 [SWFDEC_AS_ACTION_SET_TARGET2] = { "SetTarget2", 1, 0, swfdec_action_set_target2, 3 },
2834 /* version 4 */
2835 [SWFDEC_AS_ACTION_STRING_ADD] = { "StringAdd", 2, 1, swfdec_action_string_add, 4 },
2836 [SWFDEC_AS_ACTION_GET_PROPERTY] = { "GetProperty", 2, 1, swfdec_action_get_property, 4 },
2837 [SWFDEC_AS_ACTION_SET_PROPERTY] = { "SetProperty", 3, 0, swfdec_action_set_property, 4 },
2838 [SWFDEC_AS_ACTION_CLONE_SPRITE] = { "CloneSprite", 3, 0, swfdec_action_clone_sprite, 4 },
2839 [SWFDEC_AS_ACTION_REMOVE_SPRITE] = { "RemoveSprite", 1, 0, swfdec_action_remove_sprite, 4 },
2840 [SWFDEC_AS_ACTION_TRACE] = { "Trace", 1, 0, swfdec_action_trace, 4 },
2841 [SWFDEC_AS_ACTION_START_DRAG] = { "StartDrag", -1, 0, swfdec_action_start_drag, 4 },
2842 [SWFDEC_AS_ACTION_END_DRAG] = { "EndDrag", 0, 0, swfdec_action_end_drag, 4 },
2843 [SWFDEC_AS_ACTION_STRING_LESS] = { "StringLess", 2, 1, swfdec_action_string_compare, 4 },
2844 /* version 7 */
2845 [SWFDEC_AS_ACTION_THROW] = { "Throw", 1, 0, swfdec_action_throw, 7 },
2846 [SWFDEC_AS_ACTION_CAST] = { "Cast", 2, 1, swfdec_action_cast, 7 },
2847 [SWFDEC_AS_ACTION_IMPLEMENTS] = { "Implements", -1, 0, swfdec_action_implements, 7 },
2848 /* version 4 */
2849 [SWFDEC_AS_ACTION_RANDOM] = { "RandomNumber", 1, 1, swfdec_action_random_number, 4 },
2850 [SWFDEC_AS_ACTION_MB_STRING_LENGTH] = { "MBStringLength", 1, 1, swfdec_action_string_length, 4 },
2851 [SWFDEC_AS_ACTION_CHAR_TO_ASCII] = { "CharToAscii", 1, 1, swfdec_action_char_to_ascii, 4 },
2852 [SWFDEC_AS_ACTION_ASCII_TO_CHAR] = { "AsciiToChar", 1, 1, swfdec_action_ascii_to_char, 4 },
2853 [SWFDEC_AS_ACTION_GET_TIME] = { "GetTime", 0, 1, swfdec_action_get_time, 4 },
2854 [SWFDEC_AS_ACTION_MB_STRING_EXTRACT] = { "MBStringExtract", 3, 1, swfdec_action_string_extract, 4 },
2855 [SWFDEC_AS_ACTION_MB_CHAR_TO_ASCII] = { "MBCharToAscii", 1, 1, swfdec_action_char_to_ascii, 4 },
2856 [SWFDEC_AS_ACTION_MB_ASCII_TO_CHAR] = { "MBAsciiToChar", 1, 1, swfdec_action_ascii_to_char, 4 },
2857 /* version 5 */
2858 [SWFDEC_AS_ACTION_DELETE] = { "Delete", 2, 1, swfdec_action_delete, 5 },
2859 [SWFDEC_AS_ACTION_DELETE2] = { "Delete2", 1, 1, swfdec_action_delete2, 5 },
2860 [SWFDEC_AS_ACTION_DEFINE_LOCAL] = { "DefineLocal", 2, 0, swfdec_action_define_local, 5 },
2861 [SWFDEC_AS_ACTION_CALL_FUNCTION] = { "CallFunction", -1, 1, swfdec_action_call_function, 5 },
2862 [SWFDEC_AS_ACTION_RETURN] = { "Return", 1, 0, swfdec_action_return, 5 },
2863 [SWFDEC_AS_ACTION_MODULO] = { "Modulo", 2, 1, swfdec_action_modulo, 5 },
2864 [SWFDEC_AS_ACTION_NEW_OBJECT] = { "NewObject", -1, 1, swfdec_action_new_object, 5 },
2865 [SWFDEC_AS_ACTION_DEFINE_LOCAL2] = { "DefineLocal2", 1, 0, swfdec_action_define_local2, 5 },
2866 [SWFDEC_AS_ACTION_INIT_ARRAY] = { "InitArray", -1, 1, swfdec_action_init_array, 5 },
2867 [SWFDEC_AS_ACTION_INIT_OBJECT] = { "InitObject", -1, 1, swfdec_action_init_object, 5 },
2868 [SWFDEC_AS_ACTION_TYPE_OF] = { "TypeOf", 1, 1, swfdec_action_type_of, 5 },
2869 [SWFDEC_AS_ACTION_TARGET_PATH] = { "TargetPath", 1, 1, swfdec_action_target_path, 5 },
2870 [SWFDEC_AS_ACTION_ENUMERATE] = { "Enumerate", 1, -1, swfdec_action_enumerate, 5 },
2871 [SWFDEC_AS_ACTION_ADD2] = { "Add2", 2, 1, swfdec_action_add2, 5 },
2872 [SWFDEC_AS_ACTION_LESS2] = { "Less2", 2, 1, swfdec_action_new_comparison, 5 },
2873 [SWFDEC_AS_ACTION_EQUALS2] = { "Equals2", 2, 1, swfdec_action_equals2, 5 },
2874 [SWFDEC_AS_ACTION_TO_NUMBER] = { "ToNumber", 1, 1, swfdec_action_to_number, 5 },
2875 [SWFDEC_AS_ACTION_TO_STRING] = { "ToString", 1, 1, swfdec_action_to_string, 5 },
2876 [SWFDEC_AS_ACTION_PUSH_DUPLICATE] = { "PushDuplicate", 1, 2, swfdec_action_push_duplicate, 5 },
2877 [SWFDEC_AS_ACTION_SWAP] = { "Swap", 2, 2, swfdec_action_swap, 5 },
2878 /* version 4 */
2879 [SWFDEC_AS_ACTION_GET_MEMBER] = { "GetMember", 2, 1, swfdec_action_get_member, 4 },
2880 [SWFDEC_AS_ACTION_SET_MEMBER] = { "SetMember", 3, 0, swfdec_action_set_member, 4 },
2881 /* version 5 */
2882 [SWFDEC_AS_ACTION_INCREMENT] = { "Increment", 1, 1, swfdec_action_increment, 5 },
2883 [SWFDEC_AS_ACTION_DECREMENT] = { "Decrement", 1, 1, swfdec_action_decrement, 5 },
2884 [SWFDEC_AS_ACTION_CALL_METHOD] = { "CallMethod", -1, 1, swfdec_action_call_method, 5 },
2885 [SWFDEC_AS_ACTION_NEW_METHOD] = { "NewMethod", -1, 1, swfdec_action_new_method, 5 },
2886 /* version 6 */
2887 [SWFDEC_AS_ACTION_INSTANCE_OF] = { "InstanceOf", 2, 1, swfdec_action_instance_of, 6 },
2888 [SWFDEC_AS_ACTION_ENUMERATE2] = { "Enumerate2", 1, -1, swfdec_action_enumerate2, 6 },
2889 [SWFDEC_AS_ACTION_BREAKPOINT] = { "Breakpoint", -1, -1, NULL, 6 },
2890 /* version 5 */
2891 [SWFDEC_AS_ACTION_BIT_AND] = { "BitAnd", 2, 1, swfdec_action_bitwise, 5 },
2892 [SWFDEC_AS_ACTION_BIT_OR] = { "BitOr", 2, 1, swfdec_action_bitwise, 5 },
2893 [SWFDEC_AS_ACTION_BIT_XOR] = { "BitXor", 2, 1, swfdec_action_bitwise, 5 },
2894 [SWFDEC_AS_ACTION_BIT_LSHIFT] = { "BitLShift", 2, 1, swfdec_action_shift, 5 },
2895 [SWFDEC_AS_ACTION_BIT_RSHIFT] = { "BitRShift", 2, 1, swfdec_action_shift, 5 },
2896 [SWFDEC_AS_ACTION_BIT_URSHIFT] = { "BitURShift", 2, 1, swfdec_action_shift, 5 },
2897 /* version 6 */
2898 [SWFDEC_AS_ACTION_STRICT_EQUALS] = { "StrictEquals", 2, 1, swfdec_action_strict_equals, 6 },
2899 [SWFDEC_AS_ACTION_GREATER] = { "Greater", 2, 1, swfdec_action_new_comparison, 6 },
2900 [SWFDEC_AS_ACTION_STRING_GREATER] = { "StringGreater", 2, 1, swfdec_action_string_compare, 6 },
2901 /* version 7 */
2902 [SWFDEC_AS_ACTION_EXTENDS] = { "Extends", 2, 0, swfdec_action_extends, 7 },
2903 /* version 1 */
2904 [SWFDEC_AS_ACTION_GOTO_FRAME] = { "GotoFrame", 0, 0, swfdec_action_goto_frame, 1 },
2905 [SWFDEC_AS_ACTION_GET_URL] = { "GetURL", 0, 0, swfdec_action_get_url, 1 },
2906 /* version 5 */
2907 [SWFDEC_AS_ACTION_STORE_REGISTER] = { "StoreRegister", 1, 1, swfdec_action_store_register, 5 },
2908 [SWFDEC_AS_ACTION_CONSTANT_POOL] = { "ConstantPool", 0, 0, swfdec_action_constant_pool, 5 },
2909 [SWFDEC_AS_ACTION_STRICT_MODE] = { "StrictMode", -1, -1, NULL, 5 },
2910 /* version 1 */
2911 [SWFDEC_AS_ACTION_WAIT_FOR_FRAME] = { "WaitForFrame", 0, 0, swfdec_action_wait_for_frame, 1 },
2912 [SWFDEC_AS_ACTION_SET_TARGET] = { "SetTarget", 0, 0, swfdec_action_set_target, 1 },
2913 /* version 3 */
2914 [SWFDEC_AS_ACTION_GOTO_LABEL] = { "GotoLabel", 0, 0, swfdec_action_goto_label, 3 },
2915 /* version 4 */
2916 [SWFDEC_AS_ACTION_WAIT_FOR_FRAME2] = { "WaitForFrame2", 1, 0, swfdec_action_wait_for_frame2, 4 },
2917 /* version 7 */
2918 [SWFDEC_AS_ACTION_DEFINE_FUNCTION2] = { "DefineFunction2", 0, -1, swfdec_action_define_function, 7 },
2919 [SWFDEC_AS_ACTION_TRY] = { "Try", 0, 0, swfdec_action_try, 7 },
2920 /* version 5 */
2921 [SWFDEC_AS_ACTION_WITH] = { "With", 1, 0, swfdec_action_with, 5 },
2922 /* version 4 */
2923 [SWFDEC_AS_ACTION_PUSH] = { "Push", 0, -1, swfdec_action_push, 4 },
2924 [SWFDEC_AS_ACTION_JUMP] = { "Jump", 0, 0, swfdec_action_jump, 4 },
2925 [SWFDEC_AS_ACTION_GET_URL2] = { "GetURL2", 2, 0, swfdec_action_get_url2, 4 },
2926 /* version 5 */
2927 [SWFDEC_AS_ACTION_DEFINE_FUNCTION] = { "DefineFunction", 0, -1, swfdec_action_define_function, 5 },
2928 /* version 4 */
2929 [SWFDEC_AS_ACTION_IF] = { "If", 1, 0, swfdec_action_if, 4 },
2930 [SWFDEC_AS_ACTION_CALL] = { "Call", -1, -1, NULL, 4 },
2931 [SWFDEC_AS_ACTION_GOTO_FRAME2] = { "GotoFrame2", 1, 0, swfdec_action_goto_frame2, 4 }