make the g_print a debug message
[swfdec.git] / libswfdec / swfdec_as_interpret.c
blob613f59456d79f672044518e3653c881ca3739fe5
1 /* Swfdec
2 * Copyright (C) 2007 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_frame_internal.h"
28 #include "swfdec_as_function.h"
29 #include "swfdec_as_script_function.h"
30 #include "swfdec_as_stack.h"
31 #include "swfdec_as_string.h"
32 #include "swfdec_as_strings.h"
33 #include "swfdec_as_super.h"
34 #include "swfdec_as_internal.h"
35 #include "swfdec_debug.h"
37 #include <errno.h>
38 #include <math.h>
39 #include <string.h>
40 #include "swfdec_decoder.h"
41 #include "swfdec_movie.h"
42 #include "swfdec_player_internal.h"
43 #include "swfdec_sprite.h"
44 #include "swfdec_sprite_movie.h"
45 #include "swfdec_resource.h"
46 #include "swfdec_resource_request.h"
47 #include "swfdec_text_field_movie.h" // for typeof
49 /* Define this to get SWFDEC_WARN'd about missing properties of objects.
50 * This can be useful to find out about unimplemented native properties,
51 * but usually just causes a lot of spam. */
52 //#define SWFDEC_WARN_MISSING_PROPERTIES
54 /*** SUPPORT FUNCTIONS ***/
56 #define swfdec_action_has_register(cx, i) \
57 ((i) < (cx)->frame->n_registers)
59 /*** ALL THE ACTION IS HERE ***/
61 static void
62 swfdec_action_stop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
64 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
65 SWFDEC_SPRITE_MOVIE (cx->frame->target)->playing = FALSE;
66 else
67 SWFDEC_ERROR ("no movie to stop");
70 static void
71 swfdec_action_play (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
73 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
74 SWFDEC_SPRITE_MOVIE (cx->frame->target)->playing = TRUE;
75 else
76 SWFDEC_ERROR ("no movie to play");
79 static void
80 swfdec_action_next_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
82 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
83 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
84 if (movie->frame < movie->n_frames) {
85 swfdec_sprite_movie_goto (movie, movie->frame + 1);
86 } else {
87 SWFDEC_INFO ("can't execute nextFrame, already at last frame");
89 } else {
90 SWFDEC_ERROR ("no movie to nextFrame on");
94 static void
95 swfdec_action_previous_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
97 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
98 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
99 if (movie->frame > 1) {
100 swfdec_sprite_movie_goto (movie, movie->frame - 1);
101 } else {
102 SWFDEC_INFO ("can't execute previousFrame, already at first frame");
104 } else {
105 SWFDEC_ERROR ("no movie to previousFrame on");
109 static void
110 swfdec_action_goto_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
112 guint frame;
114 if (len != 2) {
115 SWFDEC_ERROR ("GotoFrame action length invalid (is %u, should be 2", len);
116 return;
118 frame = GUINT16_FROM_LE (*((guint16 *) data));
119 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
120 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
121 swfdec_sprite_movie_goto (movie, frame + 1);
122 movie->playing = FALSE;
123 } else {
124 SWFDEC_ERROR ("no movie to goto on");
128 static void
129 swfdec_action_goto_label (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
131 if (!memchr (data, 0, len)) {
132 SWFDEC_ERROR ("GotoLabel action does not specify a string");
133 return;
136 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
137 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
138 int frame;
139 if (movie->sprite == NULL ||
140 (frame = swfdec_sprite_get_frame (movie->sprite, (const char *) data)) == -1)
141 return;
142 swfdec_sprite_movie_goto (movie, frame + 1);
143 movie->playing = FALSE;
144 } else {
145 SWFDEC_ERROR ("no movie to goto on");
149 /* returns: frame to go to or 0 on error */
150 static guint
151 swfdec_value_to_frame (SwfdecAsContext *cx, SwfdecSpriteMovie *movie, SwfdecAsValue *val)
153 int frame;
155 if (movie->sprite == NULL)
156 return 0;
157 if (SWFDEC_AS_VALUE_IS_STRING (val)) {
158 const char *name = SWFDEC_AS_VALUE_GET_STRING (val);
159 double d;
160 if (strchr (name, ':')) {
161 SWFDEC_ERROR ("FIXME: handle targets");
163 /* treat valid encoded numbers as numbers, otherwise assume it's a frame label */
164 d = swfdec_as_value_to_number (cx, val);
165 if (isnan (d))
166 frame = swfdec_sprite_get_frame (movie->sprite, name) + 1;
167 else
168 frame = d;
169 } else if (SWFDEC_AS_VALUE_IS_NUMBER (val)) {
170 frame = swfdec_as_value_to_integer (cx, val);
171 } else {
172 SWFDEC_WARNING ("cannot convert value to frame number");
173 /* FIXME: how do we treat undefined etc? */
174 frame = 0;
176 return frame <= 0 ? 0 : frame;
179 static void
180 swfdec_action_goto_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
182 SwfdecBits bits;
183 guint bias;
184 gboolean play;
185 SwfdecAsValue *val;
187 swfdec_bits_init_data (&bits, data, len);
188 if (swfdec_bits_getbits (&bits, 6)) {
189 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
191 bias = swfdec_bits_getbit (&bits);
192 play = swfdec_bits_getbit (&bits);
193 if (bias) {
194 bias = swfdec_bits_get_u16 (&bits);
196 val = swfdec_as_stack_peek (cx, 1);
197 /* now set it */
198 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
199 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
200 guint frame = swfdec_value_to_frame (cx, movie, val);
201 if (frame > 0) {
202 frame += bias;
203 frame = CLAMP (frame, 1, movie->n_frames);
204 swfdec_sprite_movie_goto (movie, frame);
205 movie->playing = play;
207 } else {
208 SWFDEC_ERROR ("no movie to GotoFrame2 on");
210 swfdec_as_stack_pop (cx);
213 static void
214 swfdec_script_skip_actions (SwfdecAsContext *cx, guint jump)
216 SwfdecScript *script = cx->frame->script;
217 const guint8 *pc = cx->frame->pc;
218 const guint8 *endpc = script->buffer->data + script->buffer->length;
220 /* jump instructions */
221 do {
222 if (pc >= endpc)
223 break;
224 if (*pc & 0x80) {
225 if (pc + 2 >= endpc)
226 break;
227 pc += 3 + GUINT16_FROM_LE (*((guint16 *) (pc + 1)));
228 } else {
229 pc++;
231 } while (jump-- > 0);
232 cx->frame->pc = pc;
235 #if 0
236 static void
237 swfdec_action_wait_for_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
239 jsval val;
241 if (len != 1) {
242 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
243 return JS_FALSE;
245 val = cx->fp->sp[-1];
246 cx->fp->sp--;
247 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
248 SwfdecMovie *movie = SWFDEC_MOVIE (cx->frame->target);
249 int frame = swfdec_value_to_frame (cx, movie, val);
250 guint jump = data[2];
251 guint loaded;
252 if (frame < 0)
253 return JS_TRUE;
254 if (SWFDEC_IS_ROOT_MOVIE (movie)) {
255 SwfdecDecoder *dec = SWFDEC_ROOT_MOVIE (movie)->decoder;
256 loaded = dec->frames_loaded;
257 g_assert (loaded <= movie->n_frames);
258 } else {
259 loaded = movie->n_frames;
261 if (loaded <= (guint) frame)
262 swfdec_script_skip_actions (cx, jump);
263 } else {
264 SWFDEC_ERROR ("no movie to WaitForFrame2 on");
266 return JS_TRUE;
268 #endif
270 static void
271 swfdec_action_wait_for_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
273 SwfdecSpriteMovie *movie;
274 guint jump;
275 int frame, loaded;
277 if (len != 3) {
278 SWFDEC_ERROR ("WaitForFrame action length invalid (is %u, should be 3", len);
279 return;
281 if (!SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
282 SWFDEC_ERROR ("no movie for WaitForFrame");
283 return;
286 movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
287 frame = data[0] || (data[1] << 8);
288 jump = data[2];
289 loaded = swfdec_sprite_movie_get_frames_loaded (movie);
290 if (loaded < (int) movie->n_frames &&
291 loaded <= frame)
292 swfdec_script_skip_actions (cx, jump);
295 static void
296 swfdec_action_constant_pool (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
298 SwfdecConstantPool *pool;
299 SwfdecAsFrame *frame;
301 frame = cx->frame;
302 pool = swfdec_constant_pool_new_from_action (data, len, cx->version);
303 if (pool == NULL)
304 return;
305 swfdec_constant_pool_attach_to_context (pool, cx);
306 if (frame->constant_pool)
307 swfdec_constant_pool_free (frame->constant_pool);
308 frame->constant_pool = pool;
309 if (frame->constant_pool_buffer)
310 swfdec_buffer_unref (frame->constant_pool_buffer);
311 frame->constant_pool_buffer = swfdec_buffer_new_subbuffer (frame->script->buffer,
312 data - frame->script->buffer->data, len);
315 static void
316 swfdec_action_push (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
318 SwfdecBits bits;
320 swfdec_bits_init_data (&bits, data, len);
321 while (swfdec_bits_left (&bits)) {
322 guint type = swfdec_bits_get_u8 (&bits);
323 SWFDEC_LOG ("push type %u", type);
324 swfdec_as_stack_ensure_free (cx, 1);
325 switch (type) {
326 case 0: /* string */
328 char *s = swfdec_bits_get_string_with_version (&bits, cx->version);
329 if (s == NULL)
330 return;
331 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
332 swfdec_as_context_give_string (cx, s));
333 break;
335 case 1: /* float */
336 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_push (cx),
337 swfdec_bits_get_float (&bits));
338 break;
339 case 2: /* null */
340 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
341 break;
342 case 3: /* undefined */
343 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
344 break;
345 case 4: /* register */
347 guint regnum = swfdec_bits_get_u8 (&bits);
348 if (!swfdec_action_has_register (cx, regnum)) {
349 SWFDEC_ERROR ("cannot Push register %u: not enough registers", regnum);
350 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
351 } else {
352 *swfdec_as_stack_push (cx) = cx->frame->registers[regnum];
354 break;
356 case 5: /* boolean */
357 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx),
358 swfdec_bits_get_u8 (&bits) ? TRUE : FALSE);
359 break;
360 case 6: /* double */
361 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_push (cx),
362 swfdec_bits_get_double (&bits));
363 break;
364 case 7: /* 32bit int */
365 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_push (cx),
366 (int) swfdec_bits_get_u32 (&bits));
367 break;
368 case 8: /* 8bit ConstantPool address */
370 guint i = swfdec_bits_get_u8 (&bits);
371 SwfdecConstantPool *pool = cx->frame->constant_pool;
372 if (pool == NULL) {
373 SWFDEC_ERROR ("no constant pool to push from");
374 return;
376 if (i >= swfdec_constant_pool_size (pool)) {
377 SWFDEC_ERROR ("constant pool index %u too high - only %u elements",
378 i, swfdec_constant_pool_size (pool));
379 return;
381 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
382 swfdec_constant_pool_get (pool, i));
383 break;
385 case 9: /* 16bit ConstantPool address */
387 guint i = swfdec_bits_get_u16 (&bits);
388 SwfdecConstantPool *pool = cx->frame->constant_pool;
389 if (pool == NULL) {
390 SWFDEC_ERROR ("no constant pool to push from");
391 return;
393 if (i >= swfdec_constant_pool_size (pool)) {
394 SWFDEC_ERROR ("constant pool index %u too high - only %u elements",
395 i, swfdec_constant_pool_size (pool));
396 return;
398 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
399 swfdec_constant_pool_get (pool, i));
400 break;
402 default:
403 SWFDEC_ERROR ("Push: type %u not implemented", type);
404 return;
409 /* NB: name must be GC'd */
410 static SwfdecAsObject *
411 super_special_movie_lookup_magic (SwfdecAsContext *cx, SwfdecAsObject *o, const char *name)
413 SwfdecAsValue val;
415 if (o == NULL) {
416 o = swfdec_as_frame_get_variable (cx->frame, name, NULL);
417 if (o == NULL)
418 return NULL;
420 if (SWFDEC_IS_MOVIE (o)) {
421 SwfdecMovie *ret = swfdec_movie_get_by_name (SWFDEC_MOVIE (o), name, TRUE);
422 if (ret)
423 return SWFDEC_AS_OBJECT (ret);
425 if (!swfdec_as_object_get_variable (o, name, &val))
426 return NULL;
427 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val))
428 return NULL;
429 return SWFDEC_AS_VALUE_GET_OBJECT (&val);
432 static SwfdecAsObject *
433 swfdec_action_get_movie_by_slash_path (SwfdecAsContext *cx, const char *path)
435 SwfdecAsObject *o;
437 o = cx->frame->target;
438 if (!SWFDEC_IS_MOVIE (o))
439 return NULL;
440 if (*path == '/') {
441 o = SWFDEC_AS_OBJECT (swfdec_movie_get_root (SWFDEC_MOVIE (o)));
442 path++;
444 while (*path) {
445 char *slash = strchr (path, '/');
446 const char *name;
447 if (slash) {
448 if (slash == path)
449 return NULL;
450 name = swfdec_as_context_give_string (cx, g_strndup (path, slash - path));
451 path = slash + 1;
452 } else {
453 name = swfdec_as_context_get_string (cx, path);
454 path += strlen (path);
456 o = super_special_movie_lookup_magic (cx, o, name);
457 if (!SWFDEC_IS_MOVIE (o))
458 return NULL;
460 return o;
463 SwfdecAsObject *
464 swfdec_action_lookup_object (SwfdecAsContext *cx, SwfdecAsObject *o, const char *path, const char *end)
466 gboolean dot_allowed = TRUE;
467 const char *start;
469 if (path == end) {
470 if (o == NULL)
471 o = cx->frame->target;
472 if (SWFDEC_IS_MOVIE (o))
473 return o;
474 else
475 return NULL;
478 if (path[0] == '/') {
479 if (o == NULL)
480 o = cx->frame->target;
481 if (!SWFDEC_IS_MOVIE (o))
482 return NULL;
483 o = SWFDEC_AS_OBJECT (swfdec_movie_get_root (SWFDEC_MOVIE (o)));
484 path++;
485 dot_allowed = FALSE;
487 while (path < end) {
488 for (start = path; path < end; path++) {
489 if (dot_allowed && path[0] == '.') {
490 if (end - path >= 2 && path[1] == '.') {
491 dot_allowed = FALSE;
492 continue;
494 } else if (path[0] == ':') {
495 if (path[1] == '/')
496 continue;
497 if (path == start) {
498 start++;
499 continue;
501 } else if (path[0] == '/') {
502 dot_allowed = FALSE;
503 } else if (path - start < 127) {
504 continue;
507 break;
510 /* parse variable */
511 if (start[0] == '.' && start[1] == '.' && start + 2 == path) {
512 if (o == NULL) {
513 GSList *walk;
514 for (walk = cx->frame->scope_chain; walk; walk = walk->next) {
515 if (SWFDEC_IS_MOVIE (walk->data)) {
516 o = walk->data;
517 break;
520 if (o == NULL)
521 o = cx->frame->target;
523 /* ".." goes back to parent */
524 if (!SWFDEC_IS_MOVIE (o))
525 return NULL;
526 o = SWFDEC_AS_OBJECT (SWFDEC_MOVIE (o)->parent);
527 if (o == NULL)
528 return NULL;
529 } else {
530 o = super_special_movie_lookup_magic (cx, o,
531 swfdec_as_context_give_string (cx, g_strndup (start, path - start)));
532 if (o == NULL)
533 return NULL;
535 if (path - start < 127)
536 path++;
539 return o;
542 /* FIXME: this function belongs into swfdec_movie.c */
543 SwfdecMovie *
544 swfdec_player_get_movie_from_value (SwfdecPlayer *player, SwfdecAsValue *val)
546 SwfdecAsContext *cx;
547 const char *s;
549 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
550 g_return_val_if_fail (SWFDEC_IS_AS_VALUE (val), NULL);
552 cx = SWFDEC_AS_CONTEXT (player);
553 s = swfdec_as_value_to_string (cx, val);
554 return swfdec_player_get_movie_from_string (player, s);
557 SwfdecMovie *
558 swfdec_player_get_movie_from_string (SwfdecPlayer *player, const char *s)
560 SwfdecAsObject *ret;
562 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
563 g_return_val_if_fail (s != NULL, NULL);
565 ret = swfdec_action_lookup_object (SWFDEC_AS_CONTEXT (player), NULL, s, s + strlen (s));
566 if (!SWFDEC_IS_MOVIE (ret)) {
567 SWFDEC_WARNING ("\"%s\" does not reference a movie", s);
568 return NULL;
570 return SWFDEC_MOVIE (ret);
574 * swfdec_action_get_movie_by_path:
575 * @cx: a #SwfdecAsContext
576 * @path: the path to look up
577 * @object: pointer that takes the object that was looked up. The object may be
578 * %NULL.
579 * @variable: pointer that takes variable part of the path. The variable will
580 * be either %NULL or a non-gc'ed variable name.
582 * Looks up a Flash4-compatible path using "/", ":" and "." style syntax.
584 * Returns: The #SwfdecMovie that was looked up or %NULL if the path does not
585 * specify a valid movie.
587 static gboolean
588 swfdec_action_get_movie_by_path (SwfdecAsContext *cx, const char *path,
589 SwfdecAsObject **object, const char **variable)
591 SwfdecAsObject *movie;
592 char *end, *s;
594 g_assert (path != NULL);
595 g_assert (object != NULL);
596 g_assert (variable != NULL);
597 g_assert (cx->frame != NULL);
599 /* find dot or colon */
600 end = strpbrk (path, ".:");
602 /* if no dot or colon, look up slash-path */
603 if (end == NULL) {
604 /* shortcut for the general case */
605 if (strchr (path, '/') != NULL) {
606 movie = swfdec_action_get_movie_by_slash_path (cx, path);
607 if (movie) {
608 *object = movie;
609 *variable = NULL;
610 return TRUE;
614 *object = NULL;
615 *variable = path;
616 return TRUE;
618 /* find last dot or colon */
619 while ((s = strpbrk (end + 1, ".:")) != NULL)
620 end = s;
622 /* variable to use is the part after the last dot or colon */
623 *variable = end + 1;
624 /* look up object for start of path */
625 if (path == end)
626 movie = NULL;
627 else
628 movie = swfdec_action_lookup_object (cx, NULL, path, end);
629 if (movie) {
630 *object = movie;
631 return TRUE;
632 } else {
633 *variable = NULL;
634 return FALSE;
638 static void
639 swfdec_action_get_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
641 SwfdecAsValue *val;
642 const char *s;
643 SwfdecAsObject *object;
645 val = swfdec_as_stack_peek (cx, 1);
646 s = swfdec_as_value_to_string (cx, val);
647 if (swfdec_action_get_movie_by_path (cx, s, &object, &s)) {
648 if (object) {
649 if (s) {
650 swfdec_as_object_get_variable (object, swfdec_as_context_get_string (cx, s), val);
651 } else {
652 SWFDEC_AS_VALUE_SET_OBJECT (val, object);
654 } else {
655 swfdec_as_frame_get_variable (cx->frame, swfdec_as_context_get_string (cx, s), val);
657 } else {
658 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
659 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
660 SWFDEC_WARNING ("no variable named %s", s);
661 #endif
665 static void
666 swfdec_action_set_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
668 const char *s, *rest;
669 SwfdecAsObject *object;
671 s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
672 if (swfdec_action_get_movie_by_path (cx, s, &object, &rest)) {
673 if (object && rest) {
674 swfdec_as_object_set_variable (object, swfdec_as_context_get_string (cx, rest),
675 swfdec_as_stack_peek (cx, 1));
676 } else {
677 if (object)
678 rest = s;
679 else
680 rest = swfdec_as_context_get_string (cx, rest);
681 swfdec_as_frame_set_variable (cx->frame, rest, swfdec_as_stack_peek (cx, 1));
684 swfdec_as_stack_pop_n (cx, 2);
687 /* FIXME: this sucks */
688 extern struct {
689 gboolean needs_movie;
690 const char * name; /* GC'd */
691 void (* get) (SwfdecMovie *movie, SwfdecAsValue *ret);
692 void (* set) (SwfdecMovie *movie, const SwfdecAsValue *val);
693 } swfdec_movieclip_props[];
694 static void
695 swfdec_action_get_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
697 SwfdecMovie *movie;
698 guint id;
700 id = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
701 if (!SWFDEC_IS_PLAYER (cx)) {
702 SWFDEC_INFO ("tried using GetProperty in a non-SwfdecPlayer context");
703 goto error;
704 } else {
705 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
706 swfdec_as_stack_peek (cx, 2));
707 if (movie == NULL)
708 goto error;
710 if (id > (cx->version > 4 ? 21 : 18)) {
711 SWFDEC_WARNING ("trying to SetProperty %u, doesn't exist", id);
712 goto error;
714 swfdec_as_object_get_variable (SWFDEC_AS_OBJECT (movie), swfdec_movieclip_props[id].name,
715 swfdec_as_stack_peek (cx, 2));
716 swfdec_as_stack_pop (cx);
717 return;
719 error :
720 swfdec_as_stack_pop (cx);
721 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
724 static void
725 swfdec_action_set_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
727 SwfdecMovie *movie;
728 guint id;
730 id = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
731 if (!SWFDEC_IS_PLAYER (cx)) {
732 SWFDEC_INFO ("tried using GetProperty in a non-SwfdecPlayer context");
733 goto error;
734 } else {
735 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
736 swfdec_as_stack_peek (cx, 3));
737 if (movie == NULL)
738 goto error;
740 if (id > (cx->version > 4 ? 21 : 18)) {
741 SWFDEC_WARNING ("trying to SetProperty %u, doesn't exist", id);
742 goto error;
744 swfdec_as_object_set_variable (SWFDEC_AS_OBJECT (movie), swfdec_movieclip_props[id].name,
745 swfdec_as_stack_peek (cx, 1));
746 swfdec_as_stack_pop_n (cx, 3);
747 return;
749 error :
750 swfdec_as_stack_pop_n (cx, 3);
753 static void
754 swfdec_action_get_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
756 SwfdecAsObject *object = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 2));
757 if (object) {
758 const char *name;
759 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
760 swfdec_as_object_get_variable (object, name, swfdec_as_stack_peek (cx, 2));
761 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
762 if (SWFDEC_AS_VALUE_IS_UNDEFINED (swfdec_as_stack_peek (cx, 2))) {
763 SWFDEC_WARNING ("no variable named %s:%s", G_OBJECT_TYPE_NAME (object), name);
765 #endif
766 } else {
767 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
769 swfdec_as_stack_pop (cx);
772 static void
773 swfdec_action_set_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
775 if (SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 3))) {
776 const char *name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
777 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 3)),
778 name, swfdec_as_stack_peek (cx, 1));
780 swfdec_as_stack_pop_n (cx, 3);
783 static void
784 swfdec_action_trace (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
786 SwfdecAsValue *val;
787 const char *s;
789 val = swfdec_as_stack_peek (cx, 1);
790 if (val->type == SWFDEC_AS_TYPE_UNDEFINED) {
791 s = SWFDEC_AS_STR_undefined;
792 } else if (val->type == SWFDEC_AS_TYPE_OBJECT &&
793 SWFDEC_IS_AS_STRING (swfdec_as_value_to_object (cx, val))) {
794 s = SWFDEC_AS_STRING (swfdec_as_value_to_object (cx, val))->string;
795 } else {
796 s = swfdec_as_value_to_string (cx, val);
798 swfdec_as_stack_pop (cx);
799 g_signal_emit_by_name (cx, "trace", s);
802 /* stack looks like this: [ function, this, arg1, arg2, ... ] */
803 /* stack must be at least 2 elements big */
804 static gboolean
805 swfdec_action_call (SwfdecAsContext *cx, guint n_args)
807 SwfdecAsFunction *fun;
808 SwfdecAsObject *thisp;
810 if (!SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 1)))
811 goto error;
812 fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 1));
813 if (!SWFDEC_IS_AS_FUNCTION (fun))
814 goto error;
815 if (!SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 2))) {
816 thisp = NULL;
817 } else {
818 thisp = SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 2));
820 swfdec_as_stack_pop_n (cx, 2);
821 /* sanitize argument count */
822 if (n_args >= swfdec_as_stack_get_size (cx))
823 n_args = swfdec_as_stack_get_size (cx);
824 swfdec_as_function_call (fun, thisp, n_args, NULL, NULL);
825 if (SWFDEC_IS_AS_SUPER (fun)) {
826 SWFDEC_LOG ("replacing super object on frame");
827 swfdec_as_super_replace (SWFDEC_AS_SUPER (fun), NULL);
829 return TRUE;
831 error:
832 n_args += 2;
833 if (n_args > swfdec_as_stack_get_size (cx))
834 n_args = swfdec_as_stack_get_size (cx);
835 swfdec_as_stack_pop_n (cx, n_args);
836 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
837 return FALSE;
840 static void
841 swfdec_action_call_function (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
843 SwfdecAsFrame *frame = cx->frame;
844 SwfdecAsObject *obj;
845 guint n_args;
846 const char *name;
847 SwfdecAsValue *fun, *thisp;
849 swfdec_as_stack_ensure_size (cx, 2);
850 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
851 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
852 thisp = swfdec_as_stack_peek (cx, 2);
853 fun = swfdec_as_stack_peek (cx, 1);
854 obj = swfdec_as_frame_get_variable (frame, name, fun);
855 if (obj) {
856 SWFDEC_AS_VALUE_SET_OBJECT (thisp, obj);
857 } else {
858 SWFDEC_AS_VALUE_SET_NULL (thisp);
859 SWFDEC_AS_VALUE_SET_UNDEFINED (fun);
861 if (!swfdec_action_call (cx, n_args)) {
862 SWFDEC_WARNING ("no function named %s", name);
866 static void
867 swfdec_action_call_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
869 SwfdecAsFrame *frame = cx->frame;
870 SwfdecAsValue *val;
871 SwfdecAsObject *obj;
872 guint n_args;
873 const char *name = NULL;
875 swfdec_as_stack_ensure_size (cx, 3);
876 obj = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 2));
877 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 3));
878 val = swfdec_as_stack_peek (cx, 1);
879 /* FIXME: this is a hack for constructors calling super - is this correct? */
880 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val)) {
881 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
883 if (obj) {
884 if (SWFDEC_AS_VALUE_IS_STRING (val) &&
885 SWFDEC_AS_VALUE_GET_STRING (val) == SWFDEC_AS_STR_EMPTY) {
886 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 3));
887 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 2), obj);
888 } else {
889 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 3), obj);
890 name = swfdec_as_value_to_string (cx, val);
891 swfdec_as_object_get_variable (obj, name, swfdec_as_stack_peek (cx, 2));
893 } else {
894 if (SWFDEC_AS_VALUE_IS_STRING (val))
895 name = SWFDEC_AS_VALUE_GET_STRING (val);
896 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_peek (cx, 3));
897 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
899 swfdec_as_stack_pop (cx);
900 if (swfdec_action_call (cx, n_args)) {
901 /* setup super to point to the right prototype */
902 frame = cx->frame;
903 if (SWFDEC_IS_AS_SUPER (obj)) {
904 swfdec_as_super_replace (SWFDEC_AS_SUPER (obj), name);
905 } else if (frame->super) {
906 SwfdecAsSuper *super = SWFDEC_AS_SUPER (frame->super);
907 if (name &&
908 cx->version > 6 &&
909 swfdec_as_object_get_variable_and_flags (frame->thisp,
910 name, NULL, NULL, &super->object) &&
911 super->object == frame->thisp) {
912 // FIXME: Do we need to check prototype_flags here?
913 super->object = super->object->prototype;
916 } else {
917 SWFDEC_WARNING ("no function named %s on object %s", name ? name : "unknown", obj ? G_OBJECT_TYPE_NAME(obj) : "unknown");
921 static void
922 swfdec_action_pop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
924 swfdec_as_stack_pop (cx);
927 static void
928 swfdec_action_binary (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
930 double l, r;
932 r = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
933 l = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
934 switch (action) {
935 case SWFDEC_AS_ACTION_ADD:
936 l = l + r;
937 break;
938 case SWFDEC_AS_ACTION_SUBTRACT:
939 l = l - r;
940 break;
941 case SWFDEC_AS_ACTION_MULTIPLY:
942 l = l * r;
943 break;
944 case SWFDEC_AS_ACTION_DIVIDE:
945 if (cx->version < 5) {
946 if (r == 0) {
947 swfdec_as_stack_pop (cx);
948 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), SWFDEC_AS_STR__ERROR_);
949 return;
952 if (r == 0) {
953 if (l > 0)
954 l = INFINITY;
955 else if (l < 0)
956 l = -INFINITY;
957 else
958 l = NAN;
959 } else {
960 l = l / r;
962 break;
963 default:
964 g_assert_not_reached ();
965 break;
967 swfdec_as_stack_pop (cx);
968 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), l);
971 static void
972 swfdec_action_add2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
974 SwfdecAsValue *rval, *lval, rtmp, ltmp;
976 rval = swfdec_as_stack_peek (cx, 1);
977 lval = swfdec_as_stack_peek (cx, 2);
978 rtmp = *rval;
979 ltmp = *lval;
980 swfdec_as_value_to_primitive (&rtmp);
981 if (!SWFDEC_AS_VALUE_IS_OBJECT (&rtmp) || SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&rtmp)))
982 rval = &rtmp;
983 swfdec_as_value_to_primitive (&ltmp);
984 if (!SWFDEC_AS_VALUE_IS_OBJECT (&ltmp) || SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&ltmp)))
985 lval = &ltmp;
987 if (SWFDEC_AS_VALUE_IS_STRING (lval) || SWFDEC_AS_VALUE_IS_STRING (rval)) {
988 const char *lstr, *rstr;
989 lstr = swfdec_as_value_to_string (cx, lval);
990 rstr = swfdec_as_value_to_string (cx, rval);
991 lstr = swfdec_as_str_concat (cx, lstr, rstr);
992 swfdec_as_stack_pop (cx);
993 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), lstr);
994 } else {
995 double d, d2;
996 d = swfdec_as_value_to_number (cx, lval);
997 d2 = swfdec_as_value_to_number (cx, rval);
998 d += d2;
999 swfdec_as_stack_pop (cx);
1000 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), d);
1004 static void
1005 swfdec_action_new_comparison (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1007 SwfdecAsValue *lval, *rval;
1008 double l, r;
1010 rval = swfdec_as_stack_peek (cx, 1);
1011 lval = swfdec_as_stack_peek (cx, 2);
1013 /* swap if we do a greater comparison */
1014 if (action == SWFDEC_AS_ACTION_GREATER) {
1015 SwfdecAsValue *tmp = lval;
1016 lval = rval;
1017 rval = tmp;
1019 /* comparison with object is always false */
1020 swfdec_as_value_to_primitive (lval);
1021 if (SWFDEC_AS_VALUE_IS_OBJECT (lval) &&
1022 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (lval))) {
1023 swfdec_as_stack_pop (cx);
1024 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
1025 return;
1027 /* same for the rval */
1028 swfdec_as_value_to_primitive (rval);
1029 if (SWFDEC_AS_VALUE_IS_OBJECT (rval) &&
1030 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (rval))) {
1031 swfdec_as_stack_pop (cx);
1032 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
1033 return;
1035 /* movieclips are not objects, but they evaluate to NaN, so we can handle them here */
1036 if (SWFDEC_AS_VALUE_IS_OBJECT (rval) ||
1037 SWFDEC_AS_VALUE_IS_OBJECT (lval)) {
1038 swfdec_as_stack_pop (cx);
1039 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1040 return;
1042 /* if both are strings, compare strings */
1043 if (SWFDEC_AS_VALUE_IS_STRING (rval) &&
1044 SWFDEC_AS_VALUE_IS_STRING (lval)) {
1045 const char *ls = SWFDEC_AS_VALUE_GET_STRING (lval);
1046 const char *rs = SWFDEC_AS_VALUE_GET_STRING (rval);
1047 int cmp;
1048 if (ls == SWFDEC_AS_STR_EMPTY) {
1049 cmp = rs == SWFDEC_AS_STR_EMPTY ? 0 : 1;
1050 } else if (rs == SWFDEC_AS_STR_EMPTY) {
1051 cmp = -1;
1052 } else {
1053 cmp = strcmp (ls, rs);
1055 swfdec_as_stack_pop (cx);
1056 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cmp < 0);
1057 return;
1059 /* convert to numbers and compare those */
1060 l = swfdec_as_value_to_number (cx, lval);
1061 r = swfdec_as_value_to_number (cx, rval);
1062 swfdec_as_stack_pop (cx);
1063 /* NaN results in undefined */
1064 if (isnan (l) || isnan (r)) {
1065 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1066 return;
1068 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), l < r);
1071 static void
1072 swfdec_action_not (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1074 if (cx->version <= 4) {
1075 double d = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
1076 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), d == 0 ? 1 : 0);
1077 } else {
1078 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1),
1079 !swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1)));
1083 static void
1084 swfdec_action_jump (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1086 if (len != 2) {
1087 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
1088 return;
1090 cx->frame->pc += 5 + GINT16_FROM_LE (*((gint16*) data));
1093 static void
1094 swfdec_action_if (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1096 if (len != 2) {
1097 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
1098 return;
1100 if (swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1)))
1101 cx->frame->pc += 5 + GINT16_FROM_LE (*((gint16*) data));
1102 swfdec_as_stack_pop (cx);
1105 static void
1106 swfdec_action_decrement (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1108 SwfdecAsValue *val;
1110 val = swfdec_as_stack_peek (cx, 1);
1111 SWFDEC_AS_VALUE_SET_NUMBER (val, swfdec_as_value_to_number (cx, val) - 1);
1114 static void
1115 swfdec_action_increment (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1117 SwfdecAsValue *val;
1119 val = swfdec_as_stack_peek (cx, 1);
1120 SWFDEC_AS_VALUE_SET_NUMBER (val, swfdec_as_value_to_number (cx, val) + 1);
1123 static void
1124 swfdec_action_get_url (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1126 SwfdecBits bits;
1127 char *url, *target;
1129 swfdec_bits_init_data (&bits, data, len);
1130 url = swfdec_bits_get_string_with_version (&bits, cx->version);
1131 target = swfdec_bits_get_string_with_version (&bits, cx->version);
1132 if (url == NULL || target == NULL) {
1133 SWFDEC_ERROR ("not enough data in GetURL");
1134 g_free (url);
1135 g_free (target);
1136 return;
1138 if (swfdec_bits_left (&bits)) {
1139 SWFDEC_WARNING ("leftover bytes in GetURL action");
1141 if (!SWFDEC_IS_PLAYER (cx)) {
1142 SWFDEC_ERROR ("GetURL without a SwfdecPlayer");
1143 } else if (swfdec_player_request_fscommand (SWFDEC_PLAYER (cx), url, target)) {
1144 /* nothing to do here */
1145 } else if (swfdec_player_get_level (SWFDEC_PLAYER (cx), target) >= 0) {
1146 swfdec_resource_load (SWFDEC_PLAYER (cx), target, url,
1147 SWFDEC_LOADER_REQUEST_DEFAULT, NULL, NULL);
1149 g_free (url);
1150 g_free (target);
1153 static void
1154 swfdec_action_get_url2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1156 const char *target, *url;
1157 guint method, internal, variables;
1159 if (len != 1) {
1160 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
1161 return;
1164 method = data[0] & 3;
1165 if (method == 3) {
1166 SWFDEC_ERROR ("GetURL method 3 invalid");
1167 method = 0;
1169 internal = data[0] & 64;
1170 variables = data[0] & 128;
1171 if (method == 1 || method == 2) {
1172 SWFDEC_FIXME ("encode variables");
1175 target = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1176 url = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1178 if (!SWFDEC_IS_PLAYER (cx)) {
1179 SWFDEC_ERROR ("GetURL2 action requires a SwfdecPlayer");
1180 } else if (swfdec_player_request_fscommand (SWFDEC_PLAYER (cx), url, target)) {
1181 /* nothing to do here */
1182 } else if (variables) {
1183 SwfdecMovie *movie;
1185 movie = swfdec_player_get_movie_from_string (SWFDEC_PLAYER (cx), target);
1186 if (SWFDEC_IS_SPRITE_MOVIE (movie)) {
1187 swfdec_movie_load_variables (movie, url, method, NULL);
1189 } else if (internal) {
1190 swfdec_resource_load (SWFDEC_PLAYER (cx), target, url, method, NULL, NULL);
1191 } else {
1192 /* load an external file */
1193 swfdec_player_launch (SWFDEC_PLAYER (cx), method, url, target, NULL);
1196 swfdec_as_stack_pop_n (cx, 2);
1199 static void
1200 swfdec_action_string_add (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1202 const char *lval, *rval;
1204 rval = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1205 lval = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1206 lval = swfdec_as_str_concat (cx, lval, rval);
1207 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 2), lval);
1208 swfdec_as_stack_pop (cx);
1211 static void
1212 swfdec_action_push_duplicate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1214 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
1216 *swfdec_as_stack_push (cx) = *val;
1219 static void
1220 swfdec_action_random_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1222 gint32 max;
1223 SwfdecAsValue *val;
1225 val = swfdec_as_stack_peek (cx, 1);
1226 max = swfdec_as_value_to_integer (cx, val);
1228 if (max <= 0)
1229 SWFDEC_AS_VALUE_SET_NUMBER (val, 0);
1230 else
1231 SWFDEC_AS_VALUE_SET_NUMBER (val, g_rand_int_range (cx->rand, 0, max));
1234 static void
1235 swfdec_action_old_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1237 double l, r;
1238 gboolean cond;
1240 l = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
1241 r = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
1242 switch (action) {
1243 case SWFDEC_AS_ACTION_EQUALS:
1244 cond = l == r;
1245 break;
1246 case SWFDEC_AS_ACTION_LESS:
1247 cond = l < r;
1248 break;
1249 default:
1250 g_assert_not_reached ();
1251 return;
1253 swfdec_as_stack_pop (cx);
1254 if (cx->version < 5) {
1255 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), cond ? 1 : 0);
1256 } else {
1257 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1261 static void
1262 swfdec_action_string_extract (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1264 int start, n, left;
1265 const char *s;
1267 n = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1268 start = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1269 s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 3));
1270 swfdec_as_stack_pop_n (cx, 2);
1271 left = g_utf8_strlen (s, -1);
1272 if (start > left) {
1273 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), SWFDEC_AS_STR_EMPTY);
1274 return;
1275 } else if (start < 2) {
1276 start = 0;
1277 } else {
1278 start--;
1280 left -= start;
1281 if (n < 0 || n > left)
1282 n = left;
1284 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1),
1285 swfdec_as_str_sub (cx, s, start, n));
1288 static void
1289 swfdec_action_string_length (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1291 const char *s;
1292 SwfdecAsValue *v;
1294 v = swfdec_as_stack_peek (cx, 1);
1295 s = swfdec_as_value_to_string (cx, v);
1296 SWFDEC_AS_VALUE_SET_INT (v, g_utf8_strlen (s, -1));
1299 static void
1300 swfdec_action_string_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1302 const char *l, *r;
1303 gboolean cond;
1305 r = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1306 l = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1307 switch (action) {
1308 case SWFDEC_AS_ACTION_STRING_EQUALS:
1309 cond = l == r;
1310 break;
1311 case SWFDEC_AS_ACTION_STRING_LESS:
1312 cond = strcmp (l, r) < 0;
1313 break;
1314 default:
1315 g_assert_not_reached ();
1316 break;
1318 swfdec_as_stack_pop (cx);
1319 if (cx->version < 5) {
1320 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), cond ? 1 : 0);
1321 } else {
1322 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1326 static void
1327 swfdec_action_equals2_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1329 SwfdecAsValue *rval, *lval;
1330 SwfdecAsValue rtmp, ltmp;
1331 SwfdecAsValueType ltype, rtype;
1332 double l, r;
1333 gboolean cond;
1335 rval = swfdec_as_stack_peek (cx, 1);
1336 lval = swfdec_as_stack_peek (cx, 2);
1337 rtmp = *rval;
1338 ltmp = *lval;
1339 swfdec_as_value_to_primitive (&rtmp);
1340 swfdec_as_value_to_primitive (&ltmp);
1341 ltype = ltmp.type;
1342 rtype = rtmp.type;
1344 /* get objects compared */
1345 if (ltype == SWFDEC_AS_TYPE_OBJECT && rtype == SWFDEC_AS_TYPE_OBJECT) {
1346 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (&ltmp);
1347 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (&rtmp);
1349 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1350 lo = SWFDEC_AS_OBJECT (swfdec_movie_resolve (SWFDEC_MOVIE (lo)));
1351 ro = SWFDEC_AS_OBJECT (swfdec_movie_resolve (SWFDEC_MOVIE (ro)));
1352 } else if (SWFDEC_IS_MOVIE (lo)) {
1353 swfdec_as_value_to_primitive (rval);
1354 rtype = rval->type;
1355 if (rtype != SWFDEC_AS_TYPE_OBJECT) {
1356 cond = FALSE;
1357 goto out;
1359 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1360 } else if (SWFDEC_IS_MOVIE (ro)) {
1361 swfdec_as_value_to_primitive (lval);
1362 ltype = lval->type;
1363 if (ltype != SWFDEC_AS_TYPE_OBJECT) {
1364 cond = FALSE;
1365 goto out;
1367 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1368 } else {
1369 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1370 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1372 cond = lo == ro;
1373 goto out;
1376 /* compare strings */
1377 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1378 /* FIXME: flash 5 case insensitive? */
1379 cond = SWFDEC_AS_VALUE_GET_STRING (&ltmp) == SWFDEC_AS_VALUE_GET_STRING (&rtmp);
1380 goto out;
1383 /* convert to numbers */
1384 if (SWFDEC_AS_VALUE_IS_OBJECT (&ltmp) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&ltmp))) {
1385 l = swfdec_as_value_to_number (cx, lval);
1386 } else {
1387 l = swfdec_as_value_to_number (cx, &ltmp);
1389 if (SWFDEC_AS_VALUE_IS_OBJECT (&rtmp) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&rtmp))) {
1390 r = swfdec_as_value_to_number (cx, rval);
1391 } else {
1392 r = swfdec_as_value_to_number (cx, &rtmp);
1395 /* get rid of undefined and null */
1396 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1397 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1398 goto out;
1399 } else if (cond) {
1400 cond = FALSE;
1401 goto out;
1404 /* else compare as numbers */
1405 if (isnan (l) && isnan (r))
1406 cond = ltype == rtype;
1407 else
1408 cond = l == r;
1410 out:
1411 swfdec_as_stack_pop (cx);
1412 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1415 static void
1416 swfdec_action_equals2_6 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1418 SwfdecAsValue *rval, *lval;
1419 SwfdecAsValueType ltype, rtype;
1420 double l, r;
1421 gboolean cond;
1423 rval = swfdec_as_stack_peek (cx, 1);
1424 lval = swfdec_as_stack_peek (cx, 2);
1425 ltype = lval->type;
1426 rtype = rval->type;
1428 /* get objects compared */
1429 if (ltype == SWFDEC_AS_TYPE_OBJECT && rtype == SWFDEC_AS_TYPE_OBJECT) {
1430 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1431 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1433 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1434 lo = SWFDEC_AS_OBJECT (swfdec_movie_resolve (SWFDEC_MOVIE (lo)));
1435 ro = SWFDEC_AS_OBJECT (swfdec_movie_resolve (SWFDEC_MOVIE (ro)));
1436 } else if (SWFDEC_IS_MOVIE (lo)) {
1437 swfdec_as_value_to_primitive (rval);
1438 rtype = rval->type;
1439 if (rtype != SWFDEC_AS_TYPE_OBJECT) {
1440 cond = FALSE;
1441 goto out;
1443 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1444 } else if (SWFDEC_IS_MOVIE (ro)) {
1445 swfdec_as_value_to_primitive (lval);
1446 ltype = lval->type;
1447 if (ltype != SWFDEC_AS_TYPE_OBJECT) {
1448 cond = FALSE;
1449 goto out;
1451 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1453 cond = lo == ro;
1454 goto out;
1457 /* if one of the values is an object, call valueOf.
1458 * If it's still an object, return FALSE */
1459 swfdec_as_value_to_primitive (lval);
1460 ltype = lval->type;
1461 if (ltype == SWFDEC_AS_TYPE_OBJECT) {
1462 cond = FALSE;
1463 goto out;
1465 swfdec_as_value_to_primitive (rval);
1466 rtype = rval->type;
1467 if (rtype == SWFDEC_AS_TYPE_OBJECT) {
1468 cond = FALSE;
1469 goto out;
1471 /* now we have a comparison without objects */
1473 /* get rid of undefined and null */
1474 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1475 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1476 goto out;
1477 } else if (cond) {
1478 cond = FALSE;
1479 goto out;
1482 /* compare strings */
1483 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1484 /* FIXME: flash 5 case insensitive? */
1485 cond = SWFDEC_AS_VALUE_GET_STRING (lval) == SWFDEC_AS_VALUE_GET_STRING (rval);
1486 goto out;
1489 /* else compare as numbers */
1490 l = swfdec_as_value_to_number (cx, lval);
1491 r = swfdec_as_value_to_number (cx, rval);
1493 if (isnan (l) && isnan (r))
1494 cond = ltype == rtype;
1495 else
1496 cond = l == r;
1498 out:
1499 swfdec_as_stack_pop (cx);
1500 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1503 static void
1504 swfdec_action_equals2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1506 if (cx->version <= 5) {
1507 swfdec_action_equals2_5 (cx, action, data, len);
1508 } else {
1509 swfdec_action_equals2_6 (cx, action, data, len);
1513 static void
1514 swfdec_action_strict_equals (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1516 SwfdecAsValue *rval, *lval;
1517 gboolean cond;
1519 rval = swfdec_as_stack_peek (cx, 1);
1520 lval = swfdec_as_stack_peek (cx, 2);
1522 if (rval->type != lval->type) {
1523 cond = FALSE;
1524 } else {
1525 switch (rval->type) {
1526 case SWFDEC_AS_TYPE_UNDEFINED:
1527 case SWFDEC_AS_TYPE_NULL:
1528 cond = TRUE;
1529 break;
1530 case SWFDEC_AS_TYPE_BOOLEAN:
1531 cond = SWFDEC_AS_VALUE_GET_BOOLEAN (rval) == SWFDEC_AS_VALUE_GET_BOOLEAN (lval);
1532 break;
1533 case SWFDEC_AS_TYPE_NUMBER:
1535 double l, r;
1536 r = SWFDEC_AS_VALUE_GET_NUMBER (rval);
1537 l = SWFDEC_AS_VALUE_GET_NUMBER (lval);
1538 cond = (l == r) || (isnan (l) && isnan (r));
1540 break;
1541 case SWFDEC_AS_TYPE_STRING:
1542 cond = SWFDEC_AS_VALUE_GET_STRING (rval) == SWFDEC_AS_VALUE_GET_STRING (lval);
1543 break;
1544 case SWFDEC_AS_TYPE_OBJECT:
1546 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1547 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1548 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1549 cond = swfdec_movie_resolve (SWFDEC_MOVIE (lo)) == swfdec_movie_resolve (SWFDEC_MOVIE (ro));
1550 } else if (!SWFDEC_IS_MOVIE (lo) && !SWFDEC_IS_MOVIE (ro)) {
1551 cond = lo == ro;
1552 } else {
1553 cond = FALSE;
1556 break;
1557 case SWFDEC_AS_TYPE_INT:
1558 default:
1559 g_assert_not_reached ();
1560 cond = FALSE;
1564 swfdec_as_stack_pop (cx);
1565 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1568 static void
1569 swfdec_action_do_set_target (SwfdecAsContext *cx, const char *target, const char *end)
1571 swfdec_as_frame_set_target (cx->frame, NULL);
1573 if (target != end) {
1574 SwfdecAsObject *o = swfdec_action_lookup_object (cx, NULL, target, end);
1575 if (o == NULL) {
1576 SWFDEC_WARNING ("target \"%s\" is not an object", target);
1577 } else if (!SWFDEC_IS_MOVIE (o)) {
1578 SWFDEC_FIXME ("target \"%s\" is not a movie, something weird is supposed to happen now", target);
1579 o = NULL;
1581 swfdec_as_frame_set_target (cx->frame, o);
1585 static void
1586 swfdec_action_set_target (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1588 char *end;
1590 end = memchr (data, 0, len);
1591 if (end == NULL) {
1592 SWFDEC_ERROR ("SetTarget action does not specify a string");
1593 return;
1595 swfdec_action_do_set_target (cx, (const char *) data, end);
1598 static void
1599 swfdec_action_set_target2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1601 const char *s;
1603 s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1604 swfdec_action_do_set_target (cx, s, s + strlen (s));
1605 swfdec_as_stack_pop (cx);
1608 static void
1609 swfdec_action_start_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1611 SwfdecRect rect, *rectp = NULL;
1612 SwfdecMovie *movie;
1613 gboolean center;
1614 guint stack_size = 3;
1616 swfdec_as_stack_ensure_size (cx, 3);
1617 center = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 2));
1618 if (swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 3))) {
1619 swfdec_as_stack_ensure_size (cx, 7);
1620 rect.x0 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 7));
1621 rect.y0 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 6));
1622 rect.x1 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 5));
1623 rect.y1 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 4));
1624 swfdec_rect_scale (&rect, &rect, SWFDEC_TWIPS_SCALE_FACTOR);
1625 stack_size = 7;
1626 rectp = &rect;
1628 if (!SWFDEC_IS_PLAYER (cx)) {
1629 SWFDEC_ERROR ("called startDrag on a non-SwfdecPlayer");
1630 } else {
1631 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx), swfdec_as_stack_peek (cx, 1));
1632 if (movie != NULL) {
1633 swfdec_player_set_drag_movie (SWFDEC_PLAYER (cx), movie, center, rectp);
1634 } else {
1635 SWFDEC_ERROR ("startDrag on something not a Movie");
1638 swfdec_as_stack_pop_n (cx, stack_size);
1641 static void
1642 swfdec_action_end_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1644 if (SWFDEC_IS_PLAYER (cx)) {
1645 swfdec_player_set_drag_movie (SWFDEC_PLAYER (cx), NULL, FALSE, NULL);
1646 } else {
1647 SWFDEC_WARNING ("can't end a drag on non-players");
1651 static void
1652 swfdec_action_stop_sounds (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1654 if (SWFDEC_IS_PLAYER (cx)) {
1655 swfdec_player_stop_all_sounds (SWFDEC_PLAYER (cx));
1659 static void
1660 swfdec_action_new_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1662 SwfdecAsValue *constructor;
1663 SwfdecAsFunction *fun;
1664 guint n_args;
1666 swfdec_as_stack_ensure_size (cx, 2);
1667 swfdec_action_get_variable (cx, action, data, len);
1668 constructor = swfdec_as_stack_peek (cx, 1);
1669 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1670 n_args = MIN (swfdec_as_stack_get_size (cx) - 2, n_args);
1671 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor) ||
1672 !SWFDEC_IS_AS_FUNCTION (fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (constructor))) {
1673 SWFDEC_WARNING ("not a constructor");
1674 goto fail;
1677 swfdec_as_stack_pop_n (cx, 2);
1678 swfdec_as_object_create (fun, n_args, NULL, NULL);
1679 return;
1681 fail:
1682 swfdec_as_stack_pop_n (cx, n_args + 1);
1683 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1686 static void
1687 swfdec_action_new_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1689 SwfdecAsValue *constructor;
1690 SwfdecAsFunction *fun;
1691 guint n_args;
1692 const char *name;
1694 swfdec_as_stack_ensure_size (cx, 3);
1695 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1697 constructor = swfdec_as_stack_peek (cx, 2);
1698 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 3));
1699 n_args = MIN (swfdec_as_stack_get_size (cx) - 3, n_args);
1700 if (name != SWFDEC_AS_STR_EMPTY) {
1701 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor)) {
1702 SWFDEC_WARNING ("NewMethod called without an object to get variable %s from", name);
1703 goto fail;
1705 swfdec_as_object_get_variable (SWFDEC_AS_VALUE_GET_OBJECT (constructor),
1706 name, constructor);
1708 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor) ||
1709 !SWFDEC_IS_AS_FUNCTION (fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (constructor))) {
1710 SWFDEC_WARNING ("%s is not a constructor", name);
1711 goto fail;
1714 swfdec_as_stack_pop_n (cx, 3);
1715 swfdec_as_object_create (fun, n_args, NULL, NULL);
1716 return;
1718 fail:
1719 swfdec_as_stack_pop_n (cx, n_args + 2);
1720 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1723 static void
1724 swfdec_action_init_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1726 SwfdecAsObject *object;
1727 guint i, n_args, size;
1729 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1730 swfdec_as_stack_pop (cx);
1731 if (n_args * 2 > swfdec_as_stack_get_size (cx)) {
1732 size = swfdec_as_stack_get_size (cx);
1733 SWFDEC_FIXME ("InitObject action with too small stack, help!");
1734 n_args = size / 2;
1735 size &= 1;
1736 } else {
1737 size = 0;
1740 object = swfdec_as_object_new (cx);
1741 if (object == NULL)
1742 return;
1743 for (i = 0; i < n_args; i++) {
1744 const char *s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1745 swfdec_as_object_set_variable (object, s, swfdec_as_stack_peek (cx, 1));
1746 swfdec_as_stack_pop_n (cx, 2);
1748 swfdec_as_stack_pop_n (cx, size);
1749 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), object);
1752 static void
1753 swfdec_action_init_array (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1755 int i, n;
1756 SwfdecAsObject *array;
1758 swfdec_as_stack_ensure_size (cx, 1);
1759 n = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1760 swfdec_as_stack_pop (cx);
1761 array = swfdec_as_array_new (cx);
1762 if (array == NULL)
1763 return;
1764 /* NB: we can't increase the stack here, as the number can easily be MAXINT */
1765 for (i = 0; i < n && swfdec_as_stack_get_size (cx) > 0; i++) {
1766 swfdec_as_stack_ensure_size (cx, 1);
1767 swfdec_as_array_push (SWFDEC_AS_ARRAY (array), swfdec_as_stack_pop (cx));
1769 if (i != n) {
1770 SwfdecAsValue val;
1771 SWFDEC_AS_VALUE_SET_INT (&val, i);
1772 swfdec_as_object_set_variable (array, SWFDEC_AS_STR_length, &val);
1774 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), array);
1777 static void
1778 swfdec_action_define_function (SwfdecAsContext *cx, guint action,
1779 const guint8 *data, guint len)
1781 char *function_name;
1782 const char *name = NULL;
1783 guint i, n_args, size, n_registers;
1784 SwfdecBits bits;
1785 SwfdecBuffer *buffer;
1786 SwfdecAsFunction *fun;
1787 SwfdecAsFrame *frame;
1788 SwfdecScript *script;
1789 guint flags = 0;
1790 SwfdecScriptArgument *args;
1791 gboolean v2 = (action == 0x8e);
1793 frame = cx->frame;
1794 swfdec_bits_init_data (&bits, data, len);
1795 function_name = swfdec_bits_get_string_with_version (&bits, cx->version);
1796 if (function_name == NULL) {
1797 SWFDEC_ERROR ("could not parse function name");
1798 return;
1800 n_args = swfdec_bits_get_u16 (&bits);
1801 if (v2) {
1802 n_registers = swfdec_bits_get_u8 (&bits);
1803 if (n_registers == 0)
1804 n_registers = 4;
1805 flags = swfdec_bits_get_u16 (&bits);
1806 } else {
1807 n_registers = 5;
1809 if (n_args) {
1810 args = g_new0 (SwfdecScriptArgument, n_args);
1811 for (i = 0; i < n_args && swfdec_bits_left (&bits); i++) {
1812 if (v2) {
1813 args[i].preload = swfdec_bits_get_u8 (&bits);
1814 if (args[i].preload && args[i].preload >= n_registers) {
1815 SWFDEC_ERROR ("argument %u cannot be preloaded into register %u out of %u",
1816 i, args[i].preload, n_registers);
1817 /* FIXME: figure out correct error handling here */
1818 args[i].preload = 0;
1821 args[i].name = swfdec_bits_get_string_with_version (&bits, cx->version);
1822 if (args[i].name == NULL || args[i].name == '\0') {
1823 SWFDEC_ERROR ("empty argument name not allowed");
1824 g_free (args);
1825 return;
1827 /* FIXME: check duplicate arguments */
1829 } else {
1830 args = NULL;
1832 size = swfdec_bits_get_u16 (&bits);
1833 /* check the script can be created */
1834 if (frame->script->buffer->data + frame->script->buffer->length < frame->pc + 3 + len + size) {
1835 SWFDEC_ERROR ("size of function is too big");
1836 g_free (args);
1837 g_free (function_name);
1838 return;
1840 /* create the script */
1841 buffer = swfdec_buffer_new_subbuffer (frame->script->buffer,
1842 frame->pc + 3 + len - frame->script->buffer->data, size);
1843 swfdec_bits_init (&bits, buffer);
1844 if (*function_name) {
1845 name = function_name;
1846 } else if (swfdec_as_stack_get_size (cx) > 0) {
1847 /* This is kind of a hack that uses a feature of the Adobe compiler:
1848 * foo = function () {} is compiled as these actions:
1849 * Push "foo", DefineFunction, SetVariable/SetMember
1850 * With this knowledge we can inspect the topmost stack member, since
1851 * it will contain the name this function will soon be assigned to.
1853 if (SWFDEC_AS_VALUE_IS_STRING (swfdec_as_stack_peek (cx, 1)))
1854 name = SWFDEC_AS_VALUE_GET_STRING (swfdec_as_stack_peek (cx, 1));
1856 if (name == NULL)
1857 name = "unnamed_function";
1858 script = swfdec_script_new_from_bits (&bits, name, cx->version);
1859 swfdec_buffer_unref (buffer);
1860 if (script == NULL) {
1861 SWFDEC_ERROR ("failed to create script");
1862 g_free (args);
1863 g_free (function_name);
1864 return;
1866 if (frame->constant_pool_buffer)
1867 script->constant_pool = swfdec_buffer_ref (frame->constant_pool_buffer);
1868 script->flags = flags;
1869 script->n_registers = n_registers;
1870 script->n_arguments = n_args;
1871 script->arguments = args;
1872 /* see function-scope tests */
1873 if (cx->version > 5) {
1874 /* FIXME: or original target? */
1875 fun = swfdec_as_script_function_new (frame->original_target, frame->scope_chain, script);
1876 } else {
1877 fun = swfdec_as_script_function_new (frame->original_target, NULL, script);
1879 if (fun == NULL)
1880 return;
1881 /* This is a hack that should only trigger for functions defined in the init scripts.
1882 * It is supposed to ensure that those functions inherit their target when being
1883 * called instead of when being defined */
1884 if (!SWFDEC_IS_MOVIE (frame->original_target))
1885 SWFDEC_AS_SCRIPT_FUNCTION (fun)->target = NULL;
1886 /* attach the function */
1887 if (*function_name == '\0') {
1888 swfdec_as_stack_ensure_free (cx, 1);
1889 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), SWFDEC_AS_OBJECT (fun));
1890 } else {
1891 SwfdecAsValue funval;
1892 /* FIXME: really varobj? Not eval or sth like that? */
1893 name = swfdec_as_context_get_string (cx, function_name);
1894 SWFDEC_AS_VALUE_SET_OBJECT (&funval, SWFDEC_AS_OBJECT (fun));
1895 swfdec_as_object_set_variable (frame->target, name, &funval);
1898 /* update current context */
1899 frame->pc += 3 + len + size;
1900 g_free (function_name);
1903 static void
1904 swfdec_action_bitwise (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1906 int a, b;
1908 a = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1909 b = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1911 switch (action) {
1912 case 0x60:
1913 a = (int) (a & b);
1914 break;
1915 case 0x61:
1916 a = (int) (a | b);
1917 break;
1918 case 0x62:
1919 a = (int) (a ^ b);
1920 break;
1921 default:
1922 g_assert_not_reached ();
1923 break;
1926 swfdec_as_stack_pop (cx);
1927 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_peek (cx, 1), a);
1930 static void
1931 swfdec_action_shift (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1933 int amount, value;
1935 amount = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1936 amount &= 31;
1937 value = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1939 switch (action) {
1940 case 0x63:
1941 value = value << amount;
1942 break;
1943 case 0x64:
1944 value = ((gint) value) >> amount;
1945 break;
1946 case 0x65:
1947 value = ((guint) value) >> amount;
1948 break;
1949 default:
1950 g_assert_not_reached ();
1953 swfdec_as_stack_pop (cx);
1954 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_peek (cx, 1), value);
1957 static void
1958 swfdec_action_to_integer (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1960 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
1962 SWFDEC_AS_VALUE_SET_INT (val, swfdec_as_value_to_integer (cx, val));
1965 static void
1966 swfdec_action_target_path (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1968 SwfdecAsValue *val;
1969 SwfdecMovie *movie;
1970 char *s;
1972 val = swfdec_as_stack_peek (cx, 1);
1974 if (!SWFDEC_AS_VALUE_IS_OBJECT (val) ||
1975 !SWFDEC_IS_MOVIE (movie = (SwfdecMovie *) SWFDEC_AS_VALUE_GET_OBJECT (val))) {
1976 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
1977 return;
1979 s = swfdec_movie_get_path (movie, TRUE);
1980 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_give_string (cx, s));
1983 static void
1984 swfdec_action_define_local (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1986 SwfdecAsObject *target;
1987 const char *name;
1989 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1990 if (cx->frame->is_local) {
1991 target = SWFDEC_AS_OBJECT (cx->frame);
1992 } else {
1993 target = cx->frame->target;
1995 swfdec_as_object_set_variable (target, name,
1996 swfdec_as_stack_peek (cx, 1));
1997 swfdec_as_stack_pop_n (cx, 2);
2000 static void
2001 swfdec_action_define_local2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2003 SwfdecAsValue val = { 0, };
2004 SwfdecAsObject *target;
2005 const char *name;
2007 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
2008 if (cx->frame->is_local) {
2009 target = SWFDEC_AS_OBJECT (cx->frame);
2010 } else {
2011 target = cx->frame->target;
2013 swfdec_as_object_set_variable (target, name, &val);
2014 swfdec_as_stack_pop (cx);
2017 static void
2018 swfdec_action_return (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2020 swfdec_as_frame_return (cx->frame, swfdec_as_stack_pop (cx));
2023 static void
2024 swfdec_action_delete (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2026 SwfdecAsValue *val;
2027 const char *name;
2028 gboolean success = FALSE;
2030 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
2031 val = swfdec_as_stack_peek (cx, 2);
2032 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2033 success = swfdec_as_object_delete_variable (
2034 SWFDEC_AS_VALUE_GET_OBJECT (val), name) == SWFDEC_AS_DELETE_DELETED;
2036 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
2037 swfdec_as_stack_pop_n (cx, 1);
2040 static void
2041 swfdec_action_delete2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2043 SwfdecAsValue *val;
2044 const char *name;
2045 gboolean success = FALSE;
2047 val = swfdec_as_stack_peek (cx, 1);
2048 name = swfdec_as_value_to_string (cx, val);
2049 success = swfdec_as_frame_delete_variable (cx->frame, name) == SWFDEC_AS_DELETE_DELETED;
2050 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
2053 static void
2054 swfdec_action_store_register (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2056 if (len != 1) {
2057 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
2058 return;
2060 if (!swfdec_action_has_register (cx, *data)) {
2061 SWFDEC_ERROR ("Cannot store into register %u, not enough registers", (guint) *data);
2062 return;
2064 cx->frame->registers[*data] = *swfdec_as_stack_peek (cx, 1);
2067 static void
2068 swfdec_action_modulo (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2070 double x, y;
2072 y = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
2073 x = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
2074 /* yay, we're portable! */
2075 if (y == 0.0) {
2076 x = NAN;
2077 } else {
2078 errno = 0;
2079 x = fmod (x, y);
2080 if (errno != 0) {
2081 SWFDEC_FIXME ("errno set after fmod");
2084 swfdec_as_stack_pop (cx);
2085 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), x);
2088 static void
2089 swfdec_action_swap (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2091 swfdec_as_stack_swap (cx, 1, 2);
2094 static void
2095 swfdec_action_to_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2097 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1),
2098 swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1)));
2101 static void
2102 swfdec_action_to_string (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2104 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1),
2105 swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1)));
2108 static void
2109 swfdec_action_type_of (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2111 SwfdecAsValue *val;
2112 const char *type;
2114 val = swfdec_as_stack_peek (cx, 1);
2115 switch (val->type) {
2116 case SWFDEC_AS_TYPE_NUMBER:
2117 type = SWFDEC_AS_STR_number;
2118 break;
2119 case SWFDEC_AS_TYPE_BOOLEAN:
2120 type = SWFDEC_AS_STR_boolean;
2121 break;
2122 case SWFDEC_AS_TYPE_STRING:
2123 type = SWFDEC_AS_STR_string;
2124 break;
2125 case SWFDEC_AS_TYPE_UNDEFINED:
2126 type = SWFDEC_AS_STR_undefined;
2127 break;
2128 case SWFDEC_AS_TYPE_NULL:
2129 type = SWFDEC_AS_STR_null;
2130 break;
2131 case SWFDEC_AS_TYPE_OBJECT:
2133 SwfdecAsObject *obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
2134 if (SWFDEC_IS_MOVIE (obj)) {
2135 if (SWFDEC_IS_TEXT_FIELD_MOVIE (obj)) {
2136 type = SWFDEC_AS_STR_object;
2137 } else {
2138 type = SWFDEC_AS_STR_movieclip;
2140 } else if (SWFDEC_IS_AS_FUNCTION (obj)) {
2141 type = SWFDEC_AS_STR_function;
2142 } else {
2143 type = SWFDEC_AS_STR_object;
2146 break;
2147 case SWFDEC_AS_TYPE_INT:
2148 default:
2149 g_assert_not_reached ();
2150 type = SWFDEC_AS_STR_EMPTY;
2151 break;
2153 SWFDEC_AS_VALUE_SET_STRING (val, type);
2156 static void
2157 swfdec_action_get_time (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2159 GTimeVal tv;
2160 gulong diff;
2162 swfdec_as_context_get_time (cx, &tv);
2163 /* we assume here that swfdec_as_context_get_time always returns a tv > start_time */
2164 diff = tv.tv_sec - cx->start_time.tv_sec;
2165 if (diff > G_MAXULONG / 1000 - 1) {
2166 SWFDEC_ERROR ("FIXME: time overflow");
2168 diff *= 1000;
2169 diff = diff + (tv.tv_usec - cx->start_time.tv_usec) / 1000;
2171 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_push (cx), diff);
2174 static gboolean
2175 swfdec_action_is_instance_of (SwfdecAsObject *object,
2176 SwfdecAsObject *constructor)
2178 SwfdecAsValue val;
2179 SwfdecAsObject *class, *prototype;
2180 GSList *iter;
2182 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
2183 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (constructor), FALSE);
2185 // FIXME: propflag tests are wrong, and we shouldn't get __proto__.prototype
2186 swfdec_as_object_get_variable (constructor, SWFDEC_AS_STR_prototype, &val);
2187 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val))
2188 return FALSE;
2189 prototype = SWFDEC_AS_VALUE_GET_OBJECT (&val);
2191 class = object;
2192 while ((class = swfdec_as_object_get_prototype (class)) != NULL) {
2193 if (class == prototype)
2194 return TRUE;
2195 for (iter = class->interfaces; iter != NULL; iter = iter->next) {
2196 if (iter->data == prototype)
2197 return TRUE;
2201 return FALSE;
2204 static void
2205 swfdec_action_instance_of (SwfdecAsContext *cx, guint action,
2206 const guint8 *data, guint len)
2208 SwfdecAsValue *val;
2209 SwfdecAsObject *object, *constructor;
2211 val = swfdec_as_stack_pop (cx);
2212 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2213 constructor = SWFDEC_AS_VALUE_GET_OBJECT (val);
2214 } else {
2215 constructor = NULL;
2218 val = swfdec_as_stack_pop (cx);
2219 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2220 object = SWFDEC_AS_VALUE_GET_OBJECT (val);
2221 } else {
2222 object = NULL;
2226 if (object == NULL || constructor == NULL) {
2227 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx), FALSE);
2228 return;
2231 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx),
2232 swfdec_action_is_instance_of (object, constructor));
2235 static void
2236 swfdec_action_cast (SwfdecAsContext *cx, guint action, const guint8 *data,
2237 guint len)
2239 SwfdecAsValue *val;
2240 SwfdecAsObject *object, *constructor;
2242 val = swfdec_as_stack_pop (cx);
2243 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2244 object = SWFDEC_AS_VALUE_GET_OBJECT (val);
2245 } else {
2246 object = NULL;
2249 val = swfdec_as_stack_pop (cx);
2250 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2251 constructor = SWFDEC_AS_VALUE_GET_OBJECT (val);
2252 } else {
2253 constructor = NULL;
2256 if (object == NULL || constructor == NULL) {
2257 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
2258 return;
2261 if (swfdec_action_is_instance_of (object, constructor)) {
2262 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), object);
2263 } else {
2264 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
2268 static void
2269 swfdec_action_implements (SwfdecAsContext *cx, guint action,
2270 const guint8 *data, guint len)
2272 SwfdecAsValue *val, *argv;
2273 SwfdecAsObject *object, *proto, *interface;
2274 int argc, i;
2276 swfdec_as_stack_ensure_size (cx, 2);
2278 val = swfdec_as_stack_pop (cx);
2279 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2280 object = SWFDEC_AS_VALUE_GET_OBJECT (val);
2281 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_prototype, val);
2282 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2283 proto = SWFDEC_AS_VALUE_GET_OBJECT (val);
2284 } else {
2285 proto = NULL;
2287 } else {
2288 object = NULL;
2289 proto = NULL;
2292 val = swfdec_as_stack_pop (cx);
2293 argc = swfdec_as_value_to_integer (cx, val);
2295 if (argc > 0) {
2296 swfdec_as_stack_ensure_size (cx, argc);
2297 argv = swfdec_as_stack_pop_n (cx, argc);
2298 } else {
2299 argv = NULL;
2302 if (proto == NULL)
2303 return;
2305 for (i = 0; i < argc; i++) {
2306 if (!SWFDEC_AS_VALUE_IS_OBJECT (&argv[i]))
2307 continue;
2308 interface = SWFDEC_AS_VALUE_GET_OBJECT (&argv[i]);
2309 swfdec_as_object_get_variable (interface, SWFDEC_AS_STR_prototype, val);
2310 if (!SWFDEC_AS_VALUE_IS_OBJECT (val))
2311 continue;
2312 proto->interfaces =
2313 g_slist_prepend (proto->interfaces, SWFDEC_AS_VALUE_GET_OBJECT (val));
2317 static void
2318 swfdec_action_extends (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2320 SwfdecAsValue *superclass, *subclass, proto;
2321 SwfdecAsObject *prototype;
2322 SwfdecAsObject *super;
2324 superclass = swfdec_as_stack_peek (cx, 1);
2325 subclass = swfdec_as_stack_peek (cx, 2);
2326 if (!SWFDEC_AS_VALUE_IS_OBJECT (superclass) ||
2327 !SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (superclass))) {
2328 SWFDEC_ERROR ("superclass is not a function");
2329 goto fail;
2331 if (!SWFDEC_AS_VALUE_IS_OBJECT (subclass)) {
2332 SWFDEC_ERROR ("subclass is not an object");
2333 goto fail;
2335 super = SWFDEC_AS_VALUE_GET_OBJECT (superclass);
2336 prototype = swfdec_as_object_new_empty (cx);
2337 if (prototype == NULL)
2338 goto fail;
2339 swfdec_as_object_get_variable (super, SWFDEC_AS_STR_prototype, &proto);
2340 swfdec_as_object_set_variable (prototype, SWFDEC_AS_STR___proto__, &proto);
2341 if (cx->version > 5) {
2342 swfdec_as_object_set_variable_and_flags (prototype, SWFDEC_AS_STR___constructor__,
2343 superclass, SWFDEC_AS_VARIABLE_HIDDEN);
2345 SWFDEC_AS_VALUE_SET_OBJECT (&proto, prototype);
2346 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_OBJECT (subclass),
2347 SWFDEC_AS_STR_prototype, &proto);
2348 fail:
2349 swfdec_as_stack_pop_n (cx, 2);
2352 static gboolean
2353 swfdec_action_enumerate_foreach (SwfdecAsObject *object, const char *variable,
2354 SwfdecAsValue *value, guint flags, gpointer listp)
2356 GSList **list = listp;
2358 if (flags & SWFDEC_AS_VARIABLE_HIDDEN)
2359 return TRUE;
2361 *list = g_slist_remove (*list, variable);
2362 *list = g_slist_prepend (*list, (gpointer) variable);
2363 return TRUE;
2366 static void
2367 swfdec_action_do_enumerate (SwfdecAsContext *cx, SwfdecAsObject *object)
2369 guint i;
2370 GSList *walk, *list = NULL;
2372 for (i = 0; i < 256 && object; i++) {
2373 swfdec_as_object_foreach (object, swfdec_action_enumerate_foreach, &list);
2374 object = swfdec_as_object_get_prototype (object);
2376 if (i == 256) {
2377 swfdec_as_context_abort (object->context, "Prototype recursion limit exceeded");
2378 g_slist_free (list);
2379 return;
2381 list = g_slist_reverse (list);
2382 i = 0;
2383 for (walk = list; walk; walk = walk->next) {
2384 /* 8 is an arbitrary value */
2385 if (i % 8 == 0) {
2386 swfdec_as_stack_ensure_free (cx, 8);
2387 i = 0;
2389 i++;
2390 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx), walk->data);
2392 g_slist_free (list);
2395 static void
2396 swfdec_action_enumerate2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2398 SwfdecAsValue *val;
2399 SwfdecAsObject *obj;
2401 val = swfdec_as_stack_peek (cx, 1);
2402 if (!SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2403 SWFDEC_WARNING ("Enumerate called without an object");
2404 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
2405 return;
2407 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
2408 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
2409 swfdec_action_do_enumerate (cx, obj);
2412 static void
2413 swfdec_action_enumerate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2415 /* FIXME: make this proper functions */
2416 swfdec_action_get_variable (cx, action, data, len);
2417 swfdec_action_enumerate2 (cx, action, data, len);
2420 static void
2421 swfdec_action_logical (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2423 gboolean l, r;
2425 if (cx->version <= 4) {
2426 l = (swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1)) != 0);
2427 // don't call second parameter if not necessary
2428 if ((action == SWFDEC_AS_ACTION_AND && !l) ||
2429 (action != SWFDEC_AS_ACTION_AND && l)) {
2430 r = (swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2)) != 0);
2431 } else {
2432 r = FALSE;
2434 } else {
2435 l = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1));
2436 r = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 2));
2439 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 2),
2440 (action == SWFDEC_AS_ACTION_AND) ? (l && r) : (l || r));
2441 swfdec_as_stack_pop (cx);
2444 static void
2445 swfdec_action_char_to_ascii (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2447 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2448 const char *s = swfdec_as_value_to_string (cx, val);
2450 if (cx->version <= 5) {
2451 char *ascii = g_convert (s, -1, "LATIN1", "UTF-8", NULL, NULL, NULL);
2453 if (ascii == NULL) {
2454 /* This can happen if a Flash 5 movie gets loaded into a Flash 7 movie */
2455 SWFDEC_FIXME ("Someone threw unconvertible text %s at Flash <= 5", s);
2456 SWFDEC_AS_VALUE_SET_INT (val, 0); /* FIXME: what to return??? */
2457 } else {
2458 SWFDEC_AS_VALUE_SET_INT (val, (guchar) ascii[0]);
2459 g_free (ascii);
2461 } else {
2462 gunichar *uni = g_utf8_to_ucs4_fast (s, -1, NULL);
2464 if (uni == NULL) {
2465 /* This should never happen, everything is valid UTF-8 in here */
2466 g_warning ("conversion of character %s failed", s);
2467 SWFDEC_AS_VALUE_SET_INT (val, 0);
2468 } else {
2469 SWFDEC_AS_VALUE_SET_INT (val, uni[0]);
2470 g_free (uni);
2475 static void
2476 swfdec_action_ascii_to_char (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2478 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2480 if (cx->version <= 5) {
2481 char s[3];
2482 char *utf8;
2483 guint i;
2485 if (action == SWFDEC_AS_ACTION_ASCII_TO_CHAR) {
2486 s[0] = ((guint) swfdec_as_value_to_integer (cx, val)) % 256;
2487 s[1] = 0;
2488 } else {
2489 g_assert (action == SWFDEC_AS_ACTION_MB_ASCII_TO_CHAR);
2491 i = ((guint) swfdec_as_value_to_integer (cx, val));
2492 if (i > 255) {
2493 s[0] = i / 256;
2494 s[1] = i % 256;
2495 s[2] = 0;
2496 } else {
2497 s[0] = i;
2498 s[1] = 0;
2502 utf8 = g_convert (s, -1, "UTF-8", "LATIN1", NULL, NULL, NULL);
2503 if (utf8 == NULL) {
2504 g_warning ("conversion of character %u failed", (guint) s[0]);
2505 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2506 } else {
2507 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, utf8));
2508 g_free (utf8);
2510 } else {
2511 char *s;
2512 gunichar c = ((guint) swfdec_as_value_to_integer (cx, val)) % 65536;
2514 s = g_ucs4_to_utf8 (&c, 1, NULL, NULL, NULL);
2515 if (s == NULL) {
2516 g_warning ("conversion of character %u failed", (guint) c);
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, s));
2520 g_free (s);
2525 static void
2526 swfdec_action_throw (SwfdecAsContext *cx, guint action, const guint8 *data,
2527 guint len)
2529 swfdec_as_context_throw (cx, swfdec_as_stack_pop (cx));
2532 typedef struct {
2533 const guint8 * catch_start;
2534 const guint8 * finally_start;
2535 guint catch_size;
2536 guint finally_size;
2538 gboolean use_register;
2539 union {
2540 guint register_number;
2541 char * variable_name;
2543 } TryData;
2545 static void
2546 swfdec_action_try_data_free (gpointer data)
2548 TryData *try_data = data;
2550 g_return_if_fail (try_data != NULL);
2552 if (!try_data->use_register)
2553 g_free (try_data->variable_name);
2554 g_free (try_data);
2557 static void
2558 swfdec_action_try_end_finally (SwfdecAsFrame *frame, gpointer data)
2560 SwfdecAsValue *exception_value = data;
2561 SwfdecAsContext *cx;
2563 g_return_if_fail (SWFDEC_IS_AS_FRAME (frame));
2564 g_return_if_fail (SWFDEC_IS_AS_VALUE (exception_value));
2566 cx = SWFDEC_AS_OBJECT (frame)->context;
2568 // finally has ended and we had exception stored, throw it
2569 if (!cx->exception)
2570 swfdec_as_context_throw (cx, exception_value);
2572 g_free (data);
2575 static void
2576 swfdec_action_try_end_catch (SwfdecAsFrame *frame, gpointer data)
2578 TryData *try_data = data;
2579 SwfdecAsContext *cx;
2580 SwfdecAsValue *exception_value, val;
2582 g_return_if_fail (SWFDEC_IS_AS_FRAME (frame));
2583 g_return_if_fail (try_data != NULL);
2585 cx = SWFDEC_AS_OBJECT (frame)->context;
2587 if (swfdec_as_context_catch (cx, &val))
2589 // we got an exception while in catch block:
2590 // create new block for finally to pass on the exception
2591 // jump to that block
2593 exception_value = g_malloc (sizeof (SwfdecAsValue));
2594 *exception_value = val;
2596 // FIXME: the exception value is not marked while finally block runs
2597 swfdec_as_frame_push_block (frame, try_data->finally_start,
2598 try_data->finally_start + try_data->finally_size,
2599 swfdec_action_try_end_finally, exception_value);
2600 frame->pc = try_data->finally_start;
2603 swfdec_action_try_data_free (try_data);
2606 static void
2607 swfdec_action_try_end_try (SwfdecAsFrame *frame, gpointer data)
2609 TryData *try_data = data;
2610 SwfdecAsContext *cx;
2611 SwfdecAsValue val;
2613 g_return_if_fail (SWFDEC_IS_AS_FRAME (frame));
2614 g_return_if_fail (try_data != NULL);
2616 // if we don't have a catch block, we handle try block exactly like it was
2617 // catch block
2618 if (!try_data->catch_start) {
2619 swfdec_action_try_end_catch (frame, try_data);
2620 return;
2623 cx = SWFDEC_AS_OBJECT (frame)->context;
2625 if (swfdec_as_context_catch (cx, &val))
2627 // we got an exception while in try block:
2628 // set the exception variable
2629 // add new block for catch and jump to it
2631 if (try_data->use_register)
2633 if (swfdec_action_has_register (cx, try_data->register_number)) {
2634 cx->frame->registers[try_data->register_number] = val;
2635 } else {
2636 SWFDEC_ERROR ("cannot set Error to register %u: not enough registers",
2637 try_data->register_number);
2640 else
2642 // FIXME: this is duplicate of SetVariable
2643 SwfdecAsObject *object;
2644 const char *s, *rest;
2646 s = swfdec_as_context_get_string (cx, try_data->variable_name);
2647 if (swfdec_action_get_movie_by_path (cx, s, &object, &rest)) {
2648 if (object && rest) {
2649 swfdec_as_object_set_variable (object,
2650 swfdec_as_context_get_string (cx, rest), &val);
2651 } else {
2652 if (object) {
2653 rest = s;
2654 } else {
2655 rest = swfdec_as_context_get_string (cx, rest);
2657 swfdec_as_frame_set_variable (frame, rest, &val);
2660 else
2662 SWFDEC_ERROR ("cannot set Error to variable %s",
2663 try_data->variable_name);
2667 swfdec_as_frame_push_block (frame, try_data->catch_start,
2668 try_data->catch_start + try_data->catch_size,
2669 swfdec_action_try_end_catch, try_data);
2670 frame->pc = try_data->catch_start;
2672 else
2674 swfdec_action_try_data_free (try_data);
2678 static void
2679 swfdec_action_try (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2681 SwfdecBits bits;
2682 TryData *try_data;
2683 guint try_size;
2684 gboolean use_finally, use_catch;
2686 if (len < 8) {
2687 SWFDEC_ERROR ("With action requires a length of at least 8, but got %u",
2688 len);
2689 return;
2692 try_data = g_malloc0 (sizeof (TryData));
2694 swfdec_bits_init_data (&bits, data, len);
2696 swfdec_bits_getbits (&bits, 5); // reserved
2697 try_data->use_register = swfdec_bits_getbit (&bits);
2698 use_finally = swfdec_bits_getbit (&bits);
2699 use_catch = swfdec_bits_getbit (&bits);
2701 try_size = swfdec_bits_get_u16 (&bits);
2702 try_data->catch_size = swfdec_bits_get_u16 (&bits);
2703 try_data->finally_size = swfdec_bits_get_u16 (&bits);
2704 if (use_catch)
2705 try_data->catch_start = data + len + try_size;
2706 if (use_finally)
2707 try_data->finally_start = try_data->catch_start + try_data->catch_size;
2709 if (try_data->use_register) {
2710 try_data->register_number = swfdec_bits_get_u8 (&bits);
2711 } else {
2712 try_data->variable_name =
2713 swfdec_bits_get_string_with_version (&bits, cx->version);
2716 if (swfdec_bits_left (&bits)) {
2717 SWFDEC_WARNING ("leftover bytes in Try action");
2720 if (try_data->catch_start || try_data->finally_start) {
2721 swfdec_as_frame_push_block (cx->frame, data + len, data + len + try_size,
2722 swfdec_action_try_end_try, try_data);
2723 } else {
2724 SWFDEC_WARNING ("Try with neither catch nor finally block");
2725 swfdec_action_try_data_free (try_data);
2729 static void
2730 swfdec_action_pop_with (SwfdecAsFrame *frame, gpointer with_object)
2732 g_assert (frame->scope_chain->data == with_object);
2733 frame->scope_chain = g_slist_delete_link (frame->scope_chain, frame->scope_chain);
2736 static void
2737 swfdec_action_with (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2739 SwfdecAsObject *object;
2740 guint offset;
2742 if (len != 2) {
2743 SWFDEC_ERROR ("With action requires a length of 2, but got %u", len);
2744 swfdec_as_stack_pop (cx);
2745 return;
2747 offset = data[0] | (data[1] << 8);
2748 object = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 1));
2749 if (object == NULL) {
2750 SWFDEC_INFO ("With called without an object, skipping");
2751 cx->frame->pc = (guint8 *) data + len + offset;
2752 } else {
2753 cx->frame->scope_chain = g_slist_prepend (cx->frame->scope_chain, object);
2754 swfdec_as_frame_push_block (cx->frame, data + len, data + len + offset,
2755 swfdec_action_pop_with, object);
2757 swfdec_as_stack_pop (cx);
2760 static void
2761 swfdec_action_remove_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2763 if (!SWFDEC_IS_MOVIE (cx->frame->target)) {
2764 SWFDEC_FIXME ("target is not a movie in RemoveSprite");
2765 } else if (!SWFDEC_IS_PLAYER (cx)) {
2766 SWFDEC_INFO ("tried using RemoveSprite in a non-SwfdecPlayer context");
2767 } else {
2768 SwfdecMovie *movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
2769 swfdec_as_stack_peek (cx, 1));
2770 if (movie && swfdec_depth_classify (movie->depth) == SWFDEC_DEPTH_CLASS_DYNAMIC) {
2771 SWFDEC_LOG ("removing clip %s", movie->name);
2772 swfdec_movie_remove (movie);
2773 } else {
2774 SWFDEC_INFO ("cannot remove movie");
2777 swfdec_as_stack_pop (cx);
2780 static void
2781 swfdec_action_clone_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2783 SwfdecMovie *movie, *new_movie;
2784 const char *new_name;
2785 int depth;
2787 depth = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1)) - 16384;
2788 new_name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
2789 if (!SWFDEC_IS_MOVIE (cx->frame->target)) {
2790 SWFDEC_FIXME ("target is not a movie in CloneSprite");
2791 } else if (!SWFDEC_IS_PLAYER (cx)) {
2792 SWFDEC_INFO ("tried using CloneSprite in a non-SwfdecPlayer context");
2793 } else {
2794 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
2795 swfdec_as_stack_peek (cx, 3));
2796 if (movie == NULL) {
2797 SWFDEC_ERROR ("Object is not an SwfdecMovie object");
2798 swfdec_as_stack_pop_n (cx, 3);
2799 return;
2801 new_movie = swfdec_movie_duplicate (movie, new_name, depth);
2802 if (new_movie) {
2803 SWFDEC_LOG ("duplicated %s as %s to depth %u", movie->name, new_movie->name, new_movie->depth);
2806 swfdec_as_stack_pop_n (cx, 3);
2809 /*** PRINT FUNCTIONS ***/
2811 static char *
2812 swfdec_action_print_with (guint action, const guint8 *data, guint len)
2814 if (len != 2) {
2815 SWFDEC_ERROR ("With action requires a length of 2, but got %u", len);
2816 return NULL;
2818 return g_strdup_printf ("With %u", data[0] | (data[1] << 8));
2821 static char *
2822 swfdec_action_print_store_register (guint action, const guint8 *data, guint len)
2824 if (len != 1) {
2825 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
2826 return NULL;
2828 return g_strdup_printf ("StoreRegister %u", (guint) *data);
2831 static char *
2832 swfdec_action_print_set_target (guint action, const guint8 *data, guint len)
2834 if (!memchr (data, 0, len)) {
2835 SWFDEC_ERROR ("SetTarget action does not specify a string");
2836 return NULL;
2838 return g_strconcat ("SetTarget ", data, NULL);
2841 static char *
2842 swfdec_action_print_define_function (guint action, const guint8 *data, guint len)
2844 SwfdecBits bits;
2845 GString *string;
2846 const char *function_name;
2847 guint i, n_args, size;
2848 gboolean v2 = (action == 0x8e);
2850 string = g_string_new (v2 ? "DefineFunction2 " : "DefineFunction ");
2851 swfdec_bits_init_data (&bits, data, len);
2852 function_name = swfdec_bits_get_string (&bits);
2853 if (function_name == NULL) {
2854 SWFDEC_ERROR ("could not parse function name");
2855 g_string_free (string, TRUE);
2856 return NULL;
2858 if (*function_name) {
2859 g_string_append (string, function_name);
2860 g_string_append_c (string, ' ');
2862 n_args = swfdec_bits_get_u16 (&bits);
2863 g_string_append_c (string, '(');
2864 if (v2) {
2865 /* n_regs = */ swfdec_bits_get_u8 (&bits);
2866 /* flags = */ swfdec_bits_get_u16 (&bits);
2869 for (i = 0; i < n_args; i++) {
2870 guint preload;
2871 const char *arg_name;
2872 if (v2)
2873 preload = swfdec_bits_get_u8 (&bits);
2874 else
2875 preload = 0;
2876 arg_name = swfdec_bits_get_string (&bits);
2877 if (preload == 0 && (arg_name == NULL || *arg_name == '\0')) {
2878 SWFDEC_ERROR ("empty argument name not allowed");
2879 g_string_free (string, TRUE);
2880 return NULL;
2882 if (i)
2883 g_string_append (string, ", ");
2884 g_string_append (string, arg_name);
2885 if (preload)
2886 g_string_append_printf (string, " (%u)", preload);
2888 g_string_append_c (string, ')');
2889 size = swfdec_bits_get_u16 (&bits);
2890 g_string_append_printf (string, " %u", size);
2891 return g_string_free (string, FALSE);
2894 static char *
2895 swfdec_action_print_get_url2 (guint action, const guint8 *data, guint len)
2897 guint method;
2899 if (len != 1) {
2900 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
2901 return NULL;
2903 method = data[0] >> 6;
2904 if (method == 3) {
2905 SWFDEC_ERROR ("GetURL method 3 invalid");
2906 method = 0;
2908 if (method) {
2909 SWFDEC_FIXME ("implement encoding variables using %s", method == 1 ? "GET" : "POST");
2911 return g_strdup_printf ("GetURL2%s%s%s", method == 0 ? "" : (method == 1 ? " GET" : " POST"),
2912 data[0] & 2 ? " LoadTarget" : "", data[0] & 1 ? " LoadVariables" : "");
2915 static char *
2916 swfdec_action_print_get_url (guint action, const guint8 *data, guint len)
2918 SwfdecBits bits;
2919 char *url, *target, *ret;
2921 swfdec_bits_init_data (&bits, data, len);
2922 url = swfdec_bits_get_string (&bits);
2923 target = swfdec_bits_get_string (&bits);
2924 if (url == NULL) {
2925 SWFDEC_ERROR ("not enough data in GetURL");
2926 url = g_strdup ("???");
2928 if (target == NULL) {
2929 SWFDEC_ERROR ("not enough data in GetURL");
2930 target = g_strdup ("???");
2932 if (swfdec_bits_left (&bits)) {
2933 SWFDEC_WARNING ("leftover bytes in GetURL action");
2935 ret = g_strdup_printf ("GetURL %s %s", url, target);
2936 g_free (url);
2937 g_free (target);
2938 return ret;
2941 static char *
2942 swfdec_action_print_if (guint action, const guint8 *data, guint len)
2944 if (len != 2) {
2945 SWFDEC_ERROR ("If action length invalid (is %u, should be 2", len);
2946 return NULL;
2948 return g_strdup_printf ("If %d", GINT16_FROM_LE (*((gint16*) data)));
2951 static char *
2952 swfdec_action_print_jump (guint action, const guint8 *data, guint len)
2954 if (len != 2) {
2955 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
2956 return NULL;
2958 return g_strdup_printf ("Jump %d", GINT16_FROM_LE (*((gint16*) data)));
2961 static char *
2962 swfdec_action_print_push (guint action, const guint8 *data, guint len)
2964 gboolean first = TRUE;
2965 SwfdecBits bits;
2966 GString *string = g_string_new ("Push");
2968 swfdec_bits_init_data (&bits, data, len);
2969 while (swfdec_bits_left (&bits)) {
2970 guint type = swfdec_bits_get_u8 (&bits);
2971 if (first)
2972 g_string_append (string, " ");
2973 else
2974 g_string_append (string, ", ");
2975 first = FALSE;
2976 switch (type) {
2977 case 0: /* string */
2979 /* FIXME: need version! */
2980 char *s = swfdec_bits_get_string (&bits);
2981 if (!s) {
2982 g_string_free (string, TRUE);
2983 return NULL;
2985 g_string_append_c (string, '"');
2986 g_string_append (string, s);
2987 g_string_append_c (string, '"');
2988 g_free (s);
2989 break;
2991 case 1: /* float */
2992 g_string_append_printf (string, "%g", swfdec_bits_get_float (&bits));
2993 break;
2994 case 2: /* null */
2995 g_string_append (string, "null");
2996 break;
2997 case 3: /* undefined */
2998 g_string_append (string, "void");
2999 break;
3000 case 4: /* register */
3001 g_string_append_printf (string, "Register %u", swfdec_bits_get_u8 (&bits));
3002 break;
3003 case 5: /* boolean */
3004 g_string_append (string, swfdec_bits_get_u8 (&bits) ? "True" : "False");
3005 break;
3006 case 6: /* double */
3007 g_string_append_printf (string, "%g", swfdec_bits_get_double (&bits));
3008 break;
3009 case 7: /* 32bit int */
3010 g_string_append_printf (string, "%d", swfdec_bits_get_u32 (&bits));
3011 break;
3012 case 8: /* 8bit ConstantPool address */
3013 g_string_append_printf (string, "Pool %u", swfdec_bits_get_u8 (&bits));
3014 break;
3015 case 9: /* 16bit ConstantPool address */
3016 g_string_append_printf (string, "Pool %u", swfdec_bits_get_u16 (&bits));
3017 break;
3018 default:
3019 SWFDEC_ERROR ("Push: type %u not implemented", type);
3020 return NULL;
3023 return g_string_free (string, FALSE);
3026 /* NB: constant pool actions are special in that they are called at init time */
3027 static char *
3028 swfdec_action_print_constant_pool (guint action, const guint8 *data, guint len)
3030 guint i;
3031 GString *string;
3032 SwfdecConstantPool *pool;
3034 /* FIXME: version */
3035 pool = swfdec_constant_pool_new_from_action (data, len, 6);
3036 if (pool == NULL)
3037 return g_strdup ("ConstantPool (invalid)");
3038 string = g_string_new ("ConstantPool");
3039 for (i = 0; i < swfdec_constant_pool_size (pool); i++) {
3040 g_string_append (string, i ? ", " : " ");
3041 g_string_append (string, swfdec_constant_pool_get (pool, i));
3042 g_string_append_printf (string, " (%u)", i);
3044 return g_string_free (string, FALSE);
3047 #if 0
3048 static char *
3049 swfdec_action_print_wait_for_frame2 (guint action, const guint8 *data, guint len)
3051 if (len != 1) {
3052 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
3053 return NULL;
3055 return g_strdup_printf ("WaitForFrame2 %u", (guint) *data);
3057 #endif
3059 static char *
3060 swfdec_action_print_goto_frame2 (guint action, const guint8 *data, guint len)
3062 gboolean play, bias;
3063 SwfdecBits bits;
3065 swfdec_bits_init_data (&bits, data, len);
3066 if (swfdec_bits_getbits (&bits, 6)) {
3067 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
3069 bias = swfdec_bits_getbit (&bits);
3070 play = swfdec_bits_getbit (&bits);
3071 if (bias) {
3072 return g_strdup_printf ("GotoFrame2 %s +%u", play ? "play" : "stop",
3073 swfdec_bits_get_u16 (&bits));
3074 } else {
3075 return g_strdup_printf ("GotoFrame2 %s", play ? "play" : "stop");
3079 static char *
3080 swfdec_action_print_goto_frame (guint action, const guint8 *data, guint len)
3082 guint frame;
3084 if (len != 2)
3085 return NULL;
3087 frame = data[0] | (data[1] << 8);
3088 return g_strdup_printf ("GotoFrame %u", frame);
3091 static char *
3092 swfdec_action_print_goto_label (guint action, const guint8 *data, guint len)
3094 if (!memchr (data, 0, len)) {
3095 SWFDEC_ERROR ("GotoLabel action does not specify a string");
3096 return NULL;
3099 return g_strdup_printf ("GotoLabel %s", data);
3102 static char *
3103 swfdec_action_print_wait_for_frame (guint action, const guint8 *data, guint len)
3105 guint frame, jump;
3107 if (len != 3)
3108 return NULL;
3110 frame = data[0] | (data[1] << 8);
3111 jump = data[2];
3112 return g_strdup_printf ("WaitForFrame %u %u", frame, jump);
3115 /*** BIG FUNCTION TABLE ***/
3117 const SwfdecActionSpec swfdec_as_actions[256] = {
3118 /* version 1 */
3119 [SWFDEC_AS_ACTION_NEXT_FRAME] = { "NextFrame", NULL, 0, 0, swfdec_action_next_frame, 1 },
3120 [SWFDEC_AS_ACTION_PREVIOUS_FRAME] = { "PreviousFrame", NULL, 0, 0, swfdec_action_previous_frame, 1 },
3121 [SWFDEC_AS_ACTION_PLAY] = { "Play", NULL, 0, 0, swfdec_action_play, 1 },
3122 [SWFDEC_AS_ACTION_STOP] = { "Stop", NULL, 0, 0, swfdec_action_stop, 1 },
3123 [SWFDEC_AS_ACTION_TOGGLE_QUALITY] = { "ToggleQuality", NULL, -1, -1, NULL, 1 },
3124 /* version 2 */
3125 [SWFDEC_AS_ACTION_STOP_SOUNDS] = { "StopSounds", NULL, 0, 0, swfdec_action_stop_sounds, 2 },
3126 /* version 4 */
3127 [SWFDEC_AS_ACTION_ADD] = { "Add", NULL, 2, 1, swfdec_action_binary, 4 },
3128 [SWFDEC_AS_ACTION_SUBTRACT] = { "Subtract", NULL, 2, 1, swfdec_action_binary, 4 },
3129 [SWFDEC_AS_ACTION_MULTIPLY] = { "Multiply", NULL, 2, 1, swfdec_action_binary, 4 },
3130 [SWFDEC_AS_ACTION_DIVIDE] = { "Divide", NULL, 2, 1, swfdec_action_binary, 4 },
3131 [SWFDEC_AS_ACTION_EQUALS] = { "Equals", NULL, 2, 1, swfdec_action_old_compare, 4 },
3132 [SWFDEC_AS_ACTION_LESS] = { "Less", NULL, 2, 1, swfdec_action_old_compare, 4 },
3133 [SWFDEC_AS_ACTION_AND] = { "And", NULL, 2, 1, swfdec_action_logical, 4 },
3134 [SWFDEC_AS_ACTION_OR] = { "Or", NULL, 2, 1, swfdec_action_logical, 4 },
3135 [SWFDEC_AS_ACTION_NOT] = { "Not", NULL, 1, 1, swfdec_action_not, 4 },
3136 [SWFDEC_AS_ACTION_STRING_EQUALS] = { "StringEquals", NULL, 2, 1, swfdec_action_string_compare, 4 },
3137 [SWFDEC_AS_ACTION_STRING_LENGTH] = { "StringLength", NULL, 1, 1, swfdec_action_string_length, 4 },
3138 [SWFDEC_AS_ACTION_STRING_EXTRACT] = { "StringExtract", NULL, 3, 1, swfdec_action_string_extract, 4 },
3139 [SWFDEC_AS_ACTION_POP] = { "Pop", NULL, 1, 0, swfdec_action_pop, 4 },
3140 [SWFDEC_AS_ACTION_TO_INTEGER] = { "ToInteger", NULL, 1, 1, swfdec_action_to_integer, 4 },
3141 [SWFDEC_AS_ACTION_GET_VARIABLE] = { "GetVariable", NULL, 1, 1, swfdec_action_get_variable, 4 },
3142 [SWFDEC_AS_ACTION_SET_VARIABLE] = { "SetVariable", NULL, 2, 0, swfdec_action_set_variable, 4 },
3143 /* version 3 */
3144 [SWFDEC_AS_ACTION_SET_TARGET2] = { "SetTarget2", NULL, 1, 0, swfdec_action_set_target2, 3 },
3145 /* version 4 */
3146 [SWFDEC_AS_ACTION_STRING_ADD] = { "StringAdd", NULL, 2, 1, swfdec_action_string_add, 4 },
3147 [SWFDEC_AS_ACTION_GET_PROPERTY] = { "GetProperty", NULL, 2, 1, swfdec_action_get_property, 4 },
3148 [SWFDEC_AS_ACTION_SET_PROPERTY] = { "SetProperty", NULL, 3, 0, swfdec_action_set_property, 4 },
3149 [SWFDEC_AS_ACTION_CLONE_SPRITE] = { "CloneSprite", NULL, 3, 0, swfdec_action_clone_sprite, 4 },
3150 [SWFDEC_AS_ACTION_REMOVE_SPRITE] = { "RemoveSprite", NULL, 1, 0, swfdec_action_remove_sprite, 4 },
3151 [SWFDEC_AS_ACTION_TRACE] = { "Trace", NULL, 1, 0, swfdec_action_trace, 4 },
3152 [SWFDEC_AS_ACTION_START_DRAG] = { "StartDrag", NULL, -1, 0, swfdec_action_start_drag, 4 },
3153 [SWFDEC_AS_ACTION_END_DRAG] = { "EndDrag", NULL, 0, 0, swfdec_action_end_drag, 4 },
3154 [SWFDEC_AS_ACTION_STRING_LESS] = { "StringLess", NULL, 2, 1, swfdec_action_string_compare, 4 },
3155 /* version 7 */
3156 [SWFDEC_AS_ACTION_THROW] = { "Throw", NULL, 1, 0, swfdec_action_throw, 7 },
3157 [SWFDEC_AS_ACTION_CAST] = { "Cast", NULL, 2, 1, swfdec_action_cast, 7 },
3158 [SWFDEC_AS_ACTION_IMPLEMENTS] = { "Implements", NULL, -1, 0, swfdec_action_implements, 7 },
3159 /* version 4 */
3160 [SWFDEC_AS_ACTION_RANDOM] = { "RandomNumber", NULL, 1, 1, swfdec_action_random_number, 4 },
3161 [SWFDEC_AS_ACTION_MB_STRING_LENGTH] = { "MBStringLength", NULL, -1, -1, NULL, 4 },
3162 [SWFDEC_AS_ACTION_CHAR_TO_ASCII] = { "CharToAscii", NULL, 1, 1, swfdec_action_char_to_ascii, 4 },
3163 [SWFDEC_AS_ACTION_ASCII_TO_CHAR] = { "AsciiToChar", NULL, 1, 1, swfdec_action_ascii_to_char, 4 },
3164 [SWFDEC_AS_ACTION_GET_TIME] = { "GetTime", NULL, 0, 1, swfdec_action_get_time, 4 },
3165 [SWFDEC_AS_ACTION_MB_STRING_EXTRACT] = { "MBStringExtract", NULL, 3, 1, swfdec_action_string_extract, 4 },
3166 [SWFDEC_AS_ACTION_MB_CHAR_TO_ASCII] = { "MBCharToAscii", NULL, -1, -1, NULL, 4 },
3167 [SWFDEC_AS_ACTION_MB_ASCII_TO_CHAR] = { "MBAsciiToChar", NULL, 1, 1, swfdec_action_ascii_to_char, 4 },
3168 /* version 5 */
3169 [SWFDEC_AS_ACTION_DELETE] = { "Delete", NULL, 2, 1, swfdec_action_delete, 5 },
3170 [SWFDEC_AS_ACTION_DELETE2] = { "Delete2", NULL, 1, 1, swfdec_action_delete2, 5 },
3171 [SWFDEC_AS_ACTION_DEFINE_LOCAL] = { "DefineLocal", NULL, 2, 0, swfdec_action_define_local, 5 },
3172 [SWFDEC_AS_ACTION_CALL_FUNCTION] = { "CallFunction", NULL, -1, 1, swfdec_action_call_function, 5 },
3173 [SWFDEC_AS_ACTION_RETURN] = { "Return", NULL, 1, 0, swfdec_action_return, 5 },
3174 [SWFDEC_AS_ACTION_MODULO] = { "Modulo", NULL, 2, 1, swfdec_action_modulo, 5 },
3175 [SWFDEC_AS_ACTION_NEW_OBJECT] = { "NewObject", NULL, -1, 1, swfdec_action_new_object, 5 },
3176 [SWFDEC_AS_ACTION_DEFINE_LOCAL2] = { "DefineLocal2", NULL, 1, 0, swfdec_action_define_local2, 5 },
3177 [SWFDEC_AS_ACTION_INIT_ARRAY] = { "InitArray", NULL, -1, 1, swfdec_action_init_array, 5 },
3178 [SWFDEC_AS_ACTION_INIT_OBJECT] = { "InitObject", NULL, -1, 1, swfdec_action_init_object, 5 },
3179 [SWFDEC_AS_ACTION_TYPE_OF] = { "TypeOf", NULL, 1, 1, swfdec_action_type_of, 5 },
3180 [SWFDEC_AS_ACTION_TARGET_PATH] = { "TargetPath", NULL, 1, 1, swfdec_action_target_path, 5 },
3181 [SWFDEC_AS_ACTION_ENUMERATE] = { "Enumerate", NULL, 1, -1, swfdec_action_enumerate, 5 },
3182 [SWFDEC_AS_ACTION_ADD2] = { "Add2", NULL, 2, 1, swfdec_action_add2, 5 },
3183 [SWFDEC_AS_ACTION_LESS2] = { "Less2", NULL, 2, 1, swfdec_action_new_comparison, 5 },
3184 [SWFDEC_AS_ACTION_EQUALS2] = { "Equals2", NULL, 2, 1, swfdec_action_equals2, 5 },
3185 [SWFDEC_AS_ACTION_TO_NUMBER] = { "ToNumber", NULL, 1, 1, swfdec_action_to_number, 5 },
3186 [SWFDEC_AS_ACTION_TO_STRING] = { "ToString", NULL, 1, 1, swfdec_action_to_string, 5 },
3187 [SWFDEC_AS_ACTION_PUSH_DUPLICATE] = { "PushDuplicate", NULL, 1, 2, swfdec_action_push_duplicate, 5 },
3188 [SWFDEC_AS_ACTION_SWAP] = { "Swap", NULL, 2, 2, swfdec_action_swap, 5 },
3189 /* version 4 */
3190 [SWFDEC_AS_ACTION_GET_MEMBER] = { "GetMember", NULL, 2, 1, swfdec_action_get_member, 4 },
3191 [SWFDEC_AS_ACTION_SET_MEMBER] = { "SetMember", NULL, 3, 0, swfdec_action_set_member, 4 },
3192 /* version 5 */
3193 [SWFDEC_AS_ACTION_INCREMENT] = { "Increment", NULL, 1, 1, swfdec_action_increment, 5 },
3194 [SWFDEC_AS_ACTION_DECREMENT] = { "Decrement", NULL, 1, 1, swfdec_action_decrement, 5 },
3195 [SWFDEC_AS_ACTION_CALL_METHOD] = { "CallMethod", NULL, -1, 1, swfdec_action_call_method, 5 },
3196 [SWFDEC_AS_ACTION_NEW_METHOD] = { "NewMethod", NULL, -1, 1, swfdec_action_new_method, 5 },
3197 /* version 6 */
3198 [SWFDEC_AS_ACTION_INSTANCE_OF] = { "InstanceOf", NULL, 2, 1, swfdec_action_instance_of, 6 },
3199 [SWFDEC_AS_ACTION_ENUMERATE2] = { "Enumerate2", NULL, 1, -1, swfdec_action_enumerate2, 6 },
3200 [SWFDEC_AS_ACTION_BREAKPOINT] = { "Breakpoint", NULL, -1, -1, NULL, 6 },
3201 /* version 5 */
3202 [SWFDEC_AS_ACTION_BIT_AND] = { "BitAnd", NULL, 2, 1, swfdec_action_bitwise, 5 },
3203 [SWFDEC_AS_ACTION_BIT_OR] = { "BitOr", NULL, 2, 1, swfdec_action_bitwise, 5 },
3204 [SWFDEC_AS_ACTION_BIT_XOR] = { "BitXor", NULL, 2, 1, swfdec_action_bitwise, 5 },
3205 [SWFDEC_AS_ACTION_BIT_LSHIFT] = { "BitLShift", NULL, 2, 1, swfdec_action_shift, 5 },
3206 [SWFDEC_AS_ACTION_BIT_RSHIFT] = { "BitRShift", NULL, 2, 1, swfdec_action_shift, 5 },
3207 [SWFDEC_AS_ACTION_BIT_URSHIFT] = { "BitURShift", NULL, 2, 1, swfdec_action_shift, 5 },
3208 /* version 6 */
3209 [SWFDEC_AS_ACTION_STRICT_EQUALS] = { "StrictEquals", NULL, 2, 1, swfdec_action_strict_equals, 6 },
3210 [SWFDEC_AS_ACTION_GREATER] = { "Greater", NULL, 2, 1, swfdec_action_new_comparison, 6 },
3211 [SWFDEC_AS_ACTION_STRING_GREATER] = { "StringGreater", NULL, -1, -1, NULL, 6 },
3212 /* version 7 */
3213 [SWFDEC_AS_ACTION_EXTENDS] = { "Extends", NULL, 2, 0, swfdec_action_extends, 7 },
3214 /* version 1 */
3215 [SWFDEC_AS_ACTION_GOTO_FRAME] = { "GotoFrame", swfdec_action_print_goto_frame, 0, 0, swfdec_action_goto_frame, 1 },
3216 [SWFDEC_AS_ACTION_GET_URL] = { "GetURL", swfdec_action_print_get_url, 0, 0, swfdec_action_get_url, 1 },
3217 /* version 5 */
3218 [SWFDEC_AS_ACTION_STORE_REGISTER] = { "StoreRegister", swfdec_action_print_store_register, 1, 1, swfdec_action_store_register, 5 },
3219 [SWFDEC_AS_ACTION_CONSTANT_POOL] = { "ConstantPool", swfdec_action_print_constant_pool, 0, 0, swfdec_action_constant_pool, 5 },
3220 [SWFDEC_AS_ACTION_STRICT_MODE] = { "StrictMode", NULL, -1, -1, NULL, 5 },
3221 /* version 1 */
3222 [SWFDEC_AS_ACTION_WAIT_FOR_FRAME] = { "WaitForFrame", swfdec_action_print_wait_for_frame, 0, 0, swfdec_action_wait_for_frame, 1 },
3223 [SWFDEC_AS_ACTION_SET_TARGET] = { "SetTarget", swfdec_action_print_set_target, 0, 0, swfdec_action_set_target, 1 },
3224 /* version 3 */
3225 [SWFDEC_AS_ACTION_GOTO_LABEL] = { "GotoLabel", swfdec_action_print_goto_label, 0, 0, swfdec_action_goto_label, 3 },
3226 #if 0
3227 /* version 4 */
3228 [0x8d] = { "WaitForFrame2", swfdec_action_print_wait_for_frame2, 1, 0, { NULL, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2 } },
3229 #endif
3230 /* version 7 */
3231 [SWFDEC_AS_ACTION_DEFINE_FUNCTION2] = { "DefineFunction2", swfdec_action_print_define_function, 0, -1, swfdec_action_define_function, 7 },
3232 [SWFDEC_AS_ACTION_TRY] = { "Try", NULL, 0, 0, swfdec_action_try, 7 },
3233 /* version 5 */
3234 [SWFDEC_AS_ACTION_WITH] = { "With", swfdec_action_print_with, 1, 0, swfdec_action_with, 5 },
3235 /* version 4 */
3236 [SWFDEC_AS_ACTION_PUSH] = { "Push", swfdec_action_print_push, 0, -1, swfdec_action_push, 4 },
3237 [SWFDEC_AS_ACTION_JUMP] = { "Jump", swfdec_action_print_jump, 0, 0, swfdec_action_jump, 4 },
3238 [SWFDEC_AS_ACTION_GET_URL2] = { "GetURL2", swfdec_action_print_get_url2, 2, 0, swfdec_action_get_url2, 4 },
3239 /* version 5 */
3240 [SWFDEC_AS_ACTION_DEFINE_FUNCTION] = { "DefineFunction", swfdec_action_print_define_function, 0, -1, swfdec_action_define_function, 5 },
3241 /* version 4 */
3242 [SWFDEC_AS_ACTION_IF] = { "If", swfdec_action_print_if, 1, 0, swfdec_action_if, 4 },
3243 [SWFDEC_AS_ACTION_CALL] = { "Call", NULL, -1, -1, NULL, 4 },
3244 [SWFDEC_AS_ACTION_GOTO_FRAME2] = { "GotoFrame2", swfdec_action_print_goto_frame2, 1, 0, swfdec_action_goto_frame2, 4 }