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