make numbers garbage-collected objects
[swfdec.git] / swfdec / swfdec_as_interpret.c
blob9b6b51eb494c42effa1416eef2558db1e8126d3a
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 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
67 SWFDEC_SPRITE_MOVIE (cx->frame->target)->playing = FALSE;
68 else
69 SWFDEC_ERROR ("no movie to stop");
72 static void
73 swfdec_action_play (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
75 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
76 SWFDEC_SPRITE_MOVIE (cx->frame->target)->playing = TRUE;
77 else
78 SWFDEC_ERROR ("no movie to play");
81 static void
82 swfdec_action_next_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
84 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
85 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
86 if (movie->frame < movie->n_frames) {
87 swfdec_sprite_movie_goto (movie, movie->frame + 1);
88 movie->playing = FALSE;
89 } else {
90 SWFDEC_INFO ("can't execute nextFrame, already at last frame");
92 } else {
93 SWFDEC_ERROR ("no movie to nextFrame on");
97 static void
98 swfdec_action_previous_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
100 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
101 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
102 if (movie->frame > 1) {
103 swfdec_sprite_movie_goto (movie, movie->frame - 1);
104 movie->playing = FALSE;
105 } else {
106 SWFDEC_INFO ("can't execute previousFrame, already at first frame");
108 } else {
109 SWFDEC_ERROR ("no movie to previousFrame on");
113 static void
114 swfdec_action_goto_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
116 guint frame;
118 if (len != 2) {
119 SWFDEC_ERROR ("GotoFrame action length invalid (is %u, should be 2", len);
120 return;
122 frame = data[0] | (data[1] << 8);
123 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
124 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
125 swfdec_sprite_movie_goto (movie, frame + 1);
126 movie->playing = FALSE;
127 } else {
128 SWFDEC_ERROR ("no movie to goto on");
132 static void
133 swfdec_action_goto_label (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
135 if (!memchr (data, 0, len)) {
136 SWFDEC_ERROR ("GotoLabel action does not specify a string");
137 return;
140 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
141 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
142 int frame;
143 if (movie->sprite == NULL ||
144 (frame = swfdec_sprite_get_frame (movie->sprite, (const char *) data)) == -1)
145 return;
146 swfdec_sprite_movie_goto (movie, frame + 1);
147 movie->playing = FALSE;
148 } else {
149 SWFDEC_ERROR ("no movie to goto on");
153 /* returns: frame to go to or 0 on error */
154 static guint
155 swfdec_value_to_frame (SwfdecAsContext *cx, SwfdecSpriteMovie *movie, SwfdecAsValue *val)
157 int frame;
159 if (movie->sprite == NULL)
160 return 0;
161 if (SWFDEC_AS_VALUE_IS_STRING (val)) {
162 const char *name = SWFDEC_AS_VALUE_GET_STRING (val);
163 double d;
164 if (strchr (name, ':')) {
165 SWFDEC_ERROR ("FIXME: handle targets");
167 /* treat valid encoded numbers as numbers, otherwise assume it's a frame label */
168 d = swfdec_as_value_to_number (cx, val);
169 if (isnan (d))
170 frame = swfdec_sprite_get_frame (movie->sprite, name) + 1;
171 else
172 frame = d;
173 } else if (SWFDEC_AS_VALUE_IS_NUMBER (val)) {
174 frame = swfdec_as_value_to_integer (cx, val);
175 } else {
176 SWFDEC_WARNING ("cannot convert value to frame number");
177 /* FIXME: how do we treat undefined etc? */
178 frame = 0;
180 return frame <= 0 ? 0 : frame;
183 static void
184 swfdec_action_goto_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
186 SwfdecBits bits;
187 guint bias;
188 gboolean play;
189 SwfdecAsValue *val;
191 swfdec_bits_init_data (&bits, data, len);
192 if (swfdec_bits_getbits (&bits, 6)) {
193 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
195 bias = swfdec_bits_getbit (&bits);
196 play = swfdec_bits_getbit (&bits);
197 if (bias) {
198 bias = swfdec_bits_get_u16 (&bits);
200 val = swfdec_as_stack_peek (cx, 1);
201 /* now set it */
202 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
203 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
204 guint frame = swfdec_value_to_frame (cx, movie, val);
205 if (frame > 0) {
206 frame += bias;
207 frame = CLAMP (frame, 1, movie->n_frames);
208 swfdec_sprite_movie_goto (movie, frame);
209 movie->playing = play;
211 } else {
212 SWFDEC_ERROR ("no movie to GotoFrame2 on");
214 swfdec_as_stack_pop (cx);
217 static void
218 swfdec_script_skip_actions (SwfdecAsContext *cx, guint jump)
220 SwfdecScript *script = cx->frame->script;
221 const guint8 *pc = cx->frame->pc;
222 const guint8 *endpc = script->buffer->data + script->buffer->length;
224 /* jump instructions */
225 do {
226 if (pc >= endpc)
227 break;
228 if (*pc & 0x80) {
229 if (pc + 2 >= endpc)
230 break;
231 pc += 3 + (pc[1] | (pc[2] << 8));
232 } else {
233 pc++;
235 } while (jump-- > 0);
236 cx->frame->pc = pc;
239 static void
240 swfdec_action_wait_for_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
242 SwfdecSpriteMovie *movie;
243 int frame, loaded;
245 if (len < 1) {
246 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
247 return;
249 if (!SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
250 SWFDEC_ERROR ("no movie for WaitForFrame");
251 return;
254 movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
255 frame = swfdec_value_to_frame (cx, movie, swfdec_as_stack_pop (cx));
256 loaded = swfdec_sprite_movie_get_frames_loaded (movie);
257 if (loaded < (int) movie->n_frames &&
258 loaded < frame - 1)
259 swfdec_script_skip_actions (cx, data[0]);
262 static void
263 swfdec_action_wait_for_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
265 SwfdecSpriteMovie *movie;
266 guint jump;
267 int frame, loaded;
269 if (len != 3) {
270 SWFDEC_ERROR ("WaitForFrame action length invalid (is %u, should be 3", len);
271 return;
273 if (!SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
274 SWFDEC_ERROR ("no movie for WaitForFrame");
275 return;
278 movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
279 frame = data[0] | (data[1] << 8);
280 jump = data[2];
281 loaded = swfdec_sprite_movie_get_frames_loaded (movie);
282 if (loaded < (int) movie->n_frames &&
283 loaded < frame)
284 swfdec_script_skip_actions (cx, jump);
287 static void
288 swfdec_action_constant_pool (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
290 SwfdecConstantPool *pool;
291 SwfdecAsFrame *frame;
292 SwfdecBuffer *buffer;
294 frame = cx->frame;
295 /* FIXME: lots of hackery to get at the buffer */
296 buffer = frame->script->buffer;
297 buffer = swfdec_buffer_new_subbuffer (buffer, data - buffer->data, len);
298 pool = swfdec_constant_pool_new (cx, buffer, cx->version);
299 swfdec_buffer_unref (buffer);
300 if (pool == NULL)
301 return;
302 if (frame->constant_pool)
303 swfdec_constant_pool_unref (frame->constant_pool);
304 frame->constant_pool = pool;
307 static void
308 swfdec_action_push (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
310 SwfdecBits bits;
312 swfdec_bits_init_data (&bits, data, len);
313 while (swfdec_bits_left (&bits)) {
314 guint type = swfdec_bits_get_u8 (&bits);
315 SWFDEC_LOG ("push type %u", type);
316 swfdec_as_stack_ensure_free (cx, 1);
317 switch (type) {
318 case 0: /* string */
320 char *s = swfdec_bits_get_string (&bits, cx->version);
321 if (s == NULL) {
322 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
323 SWFDEC_AS_STR_EMPTY);
324 } else {
325 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
326 swfdec_as_context_give_string (cx, s));
328 break;
330 case 1: /* float */
331 swfdec_as_value_set_number (cx, swfdec_as_stack_push (cx),
332 swfdec_bits_get_float (&bits));
333 break;
334 case 2: /* null */
335 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
336 break;
337 case 3: /* undefined */
338 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
339 break;
340 case 4: /* register */
342 guint regnum = swfdec_bits_get_u8 (&bits);
343 if (!swfdec_action_has_register (cx, regnum)) {
344 SWFDEC_ERROR ("cannot Push register %u: not enough registers", regnum);
345 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
346 } else {
347 *swfdec_as_stack_push (cx) = cx->frame->registers[regnum];
349 break;
351 case 5: /* boolean */
352 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx),
353 swfdec_bits_get_u8 (&bits) ? TRUE : FALSE);
354 break;
355 case 6: /* double */
356 swfdec_as_value_set_number (cx, swfdec_as_stack_push (cx),
357 swfdec_bits_get_double (&bits));
358 break;
359 case 7: /* 32bit int */
360 swfdec_as_value_set_integer (cx, swfdec_as_stack_push (cx),
361 swfdec_bits_get_s32 (&bits));
362 break;
363 case 8: /* 8bit ConstantPool address */
364 case 9: /* 16bit ConstantPool address */
366 guint i = type == 8 ? swfdec_bits_get_u8 (&bits) : swfdec_bits_get_u16 (&bits);
367 SwfdecConstantPool *pool = cx->frame->constant_pool;
368 if (pool == NULL) {
369 SWFDEC_ERROR ("no constant pool to push from");
370 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
371 break;
373 if (i >= swfdec_constant_pool_size (pool)) {
374 SWFDEC_ERROR ("constant pool index %u too high - only %u elements",
375 i, swfdec_constant_pool_size (pool));
376 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
377 break;
379 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
380 swfdec_constant_pool_get (pool, i));
381 break;
383 default:
384 SWFDEC_ERROR ("Push: unknown type %u, skipping", type);
385 break;
390 /* NB: name must be GC'd */
391 static SwfdecAsObject *
392 super_special_movie_lookup_magic (SwfdecAsContext *cx, SwfdecAsObject *o, const char *name)
394 SwfdecAsValue val;
396 if (o == NULL) {
397 o = swfdec_as_frame_get_variable (cx->frame, name, NULL);
398 if (o == NULL)
399 return NULL;
401 if (SWFDEC_IS_MOVIE (o)) {
402 SwfdecMovie *ret = swfdec_movie_get_by_name (SWFDEC_MOVIE (o), name, TRUE);
403 if (ret)
404 return SWFDEC_AS_OBJECT (ret);
406 if (!swfdec_as_object_get_variable (o, name, &val))
407 return NULL;
408 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val))
409 return NULL;
410 return SWFDEC_AS_VALUE_GET_OBJECT (&val);
413 static SwfdecAsObject *
414 swfdec_action_get_movie_by_slash_path (SwfdecAsContext *cx, const char *path)
416 SwfdecAsObject *o;
418 o = cx->frame->target;
419 if (!SWFDEC_IS_MOVIE (o))
420 return NULL;
421 if (*path == '/') {
422 o = SWFDEC_AS_OBJECT (swfdec_movie_get_root (SWFDEC_MOVIE (o)));
423 path++;
425 while (*path) {
426 char *slash = strchr (path, '/');
427 const char *name;
428 if (slash) {
429 if (slash == path)
430 return NULL;
431 name = swfdec_as_context_give_string (cx, g_strndup (path, slash - path));
432 path = slash + 1;
433 } else {
434 name = swfdec_as_context_get_string (cx, path);
435 path += strlen (path);
437 o = super_special_movie_lookup_magic (cx, o, name);
438 if (!SWFDEC_IS_MOVIE (o))
439 return NULL;
441 return o;
444 SwfdecAsObject *
445 swfdec_action_lookup_object (SwfdecAsContext *cx, SwfdecAsObject *o, const char *path, const char *end)
447 gboolean dot_allowed = TRUE;
448 const char *start;
450 if (path == end) {
451 if (o == NULL)
452 o = cx->frame->target;
453 if (SWFDEC_IS_MOVIE (o))
454 return o;
455 else
456 return NULL;
459 if (path[0] == '/') {
460 if (o == NULL)
461 o = cx->frame->target;
462 if (!SWFDEC_IS_MOVIE (o))
463 return NULL;
464 o = SWFDEC_AS_OBJECT (swfdec_movie_get_root (SWFDEC_MOVIE (o)));
465 path++;
466 dot_allowed = FALSE;
468 while (path < end) {
469 for (start = path; path < end; path++) {
470 if (dot_allowed && path[0] == '.') {
471 if (end - path >= 2 && path[1] == '.') {
472 dot_allowed = FALSE;
473 continue;
475 } else if (path[0] == ':') {
476 if (path[1] == '/')
477 continue;
478 if (path == start) {
479 start++;
480 continue;
482 } else if (path[0] == '/') {
483 dot_allowed = FALSE;
484 } else if (path - start < 127) {
485 continue;
488 break;
491 /* parse variable */
492 if (start[0] == '.' && start[1] == '.' && start + 2 == path) {
493 if (o == NULL) {
494 GSList *walk;
495 for (walk = cx->frame->scope_chain; walk; walk = walk->next) {
496 if (SWFDEC_IS_MOVIE (walk->data)) {
497 o = walk->data;
498 break;
501 if (o == NULL)
502 o = cx->frame->target;
504 /* ".." goes back to parent */
505 if (!SWFDEC_IS_MOVIE (o))
506 return NULL;
507 o = SWFDEC_AS_OBJECT (SWFDEC_MOVIE (o)->parent);
508 if (o == NULL)
509 return NULL;
510 } else {
511 o = super_special_movie_lookup_magic (cx, o,
512 swfdec_as_context_give_string (cx, g_strndup (start, path - start)));
513 if (o == NULL)
514 return NULL;
516 if (path - start < 127)
517 path++;
520 return o;
523 /* FIXME: this function belongs into swfdec_movie.c */
524 SwfdecMovie *
525 swfdec_player_get_movie_from_value (SwfdecPlayer *player, SwfdecAsValue *val)
527 SwfdecAsContext *cx;
528 const char *s;
530 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
531 g_return_val_if_fail (SWFDEC_IS_AS_VALUE (val), NULL);
533 cx = SWFDEC_AS_CONTEXT (player);
534 s = swfdec_as_value_to_string (cx, val);
535 return swfdec_player_get_movie_from_string (player, s);
538 SwfdecMovie *
539 swfdec_player_get_movie_from_string (SwfdecPlayer *player, const char *s)
541 SwfdecAsObject *ret;
543 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
544 g_return_val_if_fail (s != NULL, NULL);
546 ret = swfdec_action_lookup_object (SWFDEC_AS_CONTEXT (player), NULL, s, s + strlen (s));
547 if (!SWFDEC_IS_MOVIE (ret)) {
548 SWFDEC_WARNING ("\"%s\" does not reference a movie", s);
549 return NULL;
551 return SWFDEC_MOVIE (ret);
555 * swfdec_action_get_movie_by_path:
556 * @cx: a #SwfdecAsContext
557 * @path: the path to look up
558 * @object: pointer that takes the object that was looked up. The object may be
559 * %NULL.
560 * @variable: pointer that takes variable part of the path. The variable will
561 * be either %NULL or a non-gc'ed variable name.
563 * Looks up a Flash4-compatible path using "/", ":" and "." style syntax.
565 * Returns: The #SwfdecMovie that was looked up or %NULL if the path does not
566 * specify a valid movie.
568 static gboolean
569 swfdec_action_get_movie_by_path (SwfdecAsContext *cx, const char *path,
570 SwfdecAsObject **object, const char **variable)
572 SwfdecAsObject *movie;
573 char *end, *s;
575 g_assert (path != NULL);
576 g_assert (object != NULL);
577 g_assert (variable != NULL);
578 g_assert (cx->frame != NULL);
580 /* find dot or colon */
581 end = strpbrk (path, ".:");
583 /* if no dot or colon, look up slash-path */
584 if (end == NULL) {
585 /* shortcut for the general case */
586 if (strchr (path, '/') != NULL) {
587 movie = swfdec_action_get_movie_by_slash_path (cx, path);
588 if (movie) {
589 *object = movie;
590 *variable = NULL;
591 return TRUE;
595 *object = NULL;
596 *variable = path;
597 return TRUE;
599 /* find last dot or colon */
600 while ((s = strpbrk (end + 1, ".:")) != NULL)
601 end = s;
603 /* variable to use is the part after the last dot or colon */
604 *variable = end + 1;
605 /* look up object for start of path */
606 if (path == end)
607 movie = NULL;
608 else
609 movie = swfdec_action_lookup_object (cx, NULL, path, end);
610 if (movie) {
611 *object = movie;
612 return TRUE;
613 } else {
614 *variable = NULL;
615 return FALSE;
619 static void
620 swfdec_action_get_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
622 SwfdecAsValue *val;
623 const char *s;
624 SwfdecAsObject *object;
626 val = swfdec_as_stack_peek (cx, 1);
627 s = swfdec_as_value_to_string (cx, val);
628 if (swfdec_action_get_movie_by_path (cx, s, &object, &s)) {
629 if (object) {
630 if (s) {
631 swfdec_as_object_get_variable (object, swfdec_as_context_get_string (cx, s), val);
632 } else {
633 SWFDEC_AS_VALUE_SET_OBJECT (val, object);
635 } else {
636 swfdec_as_frame_get_variable (cx->frame, swfdec_as_context_get_string (cx, s), val);
638 } else {
639 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
640 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
641 SWFDEC_WARNING ("no variable named %s", s);
642 #endif
646 static void
647 swfdec_action_set_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
649 const char *s, *rest;
650 SwfdecAsObject *object;
652 s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
653 if (swfdec_action_get_movie_by_path (cx, s, &object, &rest)) {
654 if (object && rest) {
655 swfdec_as_object_set_variable (object, swfdec_as_context_get_string (cx, rest),
656 swfdec_as_stack_peek (cx, 1));
657 } else {
658 if (object)
659 rest = s;
660 else
661 rest = swfdec_as_context_get_string (cx, rest);
662 swfdec_as_frame_set_variable (cx->frame, rest,
663 swfdec_as_stack_peek (cx, 1), TRUE, FALSE);
666 swfdec_as_stack_pop_n (cx, 2);
669 static void
670 swfdec_action_get_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
672 SwfdecMovie *movie;
673 guint id;
675 id = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
676 if (!SWFDEC_IS_PLAYER (cx)) {
677 SWFDEC_INFO ("tried using GetProperty in a non-SwfdecPlayer context");
678 movie = NULL;
679 } else {
680 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
681 swfdec_as_stack_peek (cx, 2));
683 if (movie == NULL) {
684 SWFDEC_ERROR ("calling GetProperty not on a movieclip object");
685 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
686 } else if (id > (cx->version > 4 ? 21 : 18)) {
687 SWFDEC_WARNING ("trying to GetProperty %u, doesn't exist", id);
688 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
689 } else {
690 swfdec_movie_property_get (movie, id, swfdec_as_stack_peek (cx, 2));
692 swfdec_as_stack_pop (cx);
695 static void
696 swfdec_action_set_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, 2));
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, 3));
709 if (movie == NULL) {
710 SWFDEC_ERROR ("calling GetProperty not on a movieclip object");
711 } else if (id > (cx->version > 4 ? 21 : 18)) {
712 SWFDEC_WARNING ("trying to SetProperty %u, doesn't exist", id);
713 } else {
714 swfdec_movie_property_set (movie, id, swfdec_as_stack_peek (cx, 1));
716 swfdec_as_stack_pop_n (cx, 3);
719 static void
720 swfdec_action_get_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
722 SwfdecAsObject *object = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 2));
723 if (object) {
724 const char *name;
725 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
726 swfdec_as_object_get_variable (object, name, swfdec_as_stack_peek (cx, 2));
727 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
728 if (SWFDEC_AS_VALUE_IS_UNDEFINED (swfdec_as_stack_peek (cx, 2))) {
729 SWFDEC_WARNING ("no variable named %s:%s", G_OBJECT_TYPE_NAME (object), name);
731 #endif
732 } else {
733 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
735 swfdec_as_stack_pop (cx);
738 static void
739 swfdec_action_set_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
741 if (SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 3))) {
742 const char *name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
743 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 3)),
744 name, swfdec_as_stack_peek (cx, 1));
746 swfdec_as_stack_pop_n (cx, 3);
749 static void
750 swfdec_action_trace (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
752 SwfdecAsValue *val;
753 const char *s;
755 val = swfdec_as_stack_peek (cx, 1);
756 if (val->type == SWFDEC_AS_TYPE_UNDEFINED) {
757 s = SWFDEC_AS_STR_undefined;
758 } else {
759 s = swfdec_as_value_to_string (cx, val);
761 swfdec_as_stack_pop (cx);
762 g_signal_emit_by_name (cx, "trace", s);
765 /* stack looks like this: [ function, this, arg1, arg2, ... ] */
766 /* stack must be at least 2 elements big */
767 static gboolean
768 swfdec_action_call (SwfdecAsContext *cx, guint n_args, SwfdecAsObject *super)
770 SwfdecAsFunction *fun;
771 SwfdecAsObject *thisp;
773 if (!SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 1)))
774 goto error;
775 fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 1));
776 if (!SWFDEC_IS_AS_FUNCTION (fun))
777 goto error;
778 if (!SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 2))) {
779 thisp = NULL;
780 } else {
781 thisp = SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 2));
783 swfdec_as_stack_pop_n (cx, 2);
784 /* sanitize argument count */
785 if (n_args >= swfdec_as_stack_get_size (cx))
786 n_args = swfdec_as_stack_get_size (cx);
787 if (super == NULL && SWFDEC_IS_AS_SUPER (fun)) {
788 SWFDEC_LOG ("replacing super object on frame");
789 super = swfdec_as_super_resolve_property (SWFDEC_AS_SUPER (fun), NULL);
791 swfdec_as_function_call_full (fun, thisp, FALSE, super, n_args, NULL, NULL);
792 return TRUE;
794 error:
795 n_args += 2;
796 if (n_args > swfdec_as_stack_get_size (cx))
797 n_args = swfdec_as_stack_get_size (cx);
798 swfdec_as_stack_pop_n (cx, n_args);
799 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
800 return FALSE;
803 static void
804 swfdec_action_call_function (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
806 SwfdecAsFrame *frame = cx->frame;
807 SwfdecAsObject *obj;
808 guint n_args;
809 const char *name;
810 SwfdecAsValue *fun, *thisp;
812 swfdec_as_stack_ensure_size (cx, 2);
813 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
814 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
815 thisp = swfdec_as_stack_peek (cx, 2);
816 fun = swfdec_as_stack_peek (cx, 1);
817 obj = swfdec_as_frame_get_variable (frame, name, fun);
818 if (obj) {
819 SWFDEC_AS_VALUE_SET_OBJECT (thisp, obj);
820 } else {
821 SWFDEC_AS_VALUE_SET_NULL (thisp);
822 SWFDEC_AS_VALUE_SET_UNDEFINED (fun);
824 if (!swfdec_action_call (cx, n_args, NULL)) {
825 SWFDEC_WARNING ("no function named %s", name);
829 static void
830 swfdec_action_call_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
832 SwfdecAsValue *val;
833 SwfdecAsObject *obj, *super;
834 SwfdecAsObject *pobj = NULL;
835 guint n_args;
836 const char *name;
838 swfdec_as_stack_ensure_size (cx, 3);
839 obj = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 2));
840 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 3));
841 val = swfdec_as_stack_peek (cx, 1);
842 if (obj) {
843 name = swfdec_as_value_to_string (cx, val);
844 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val) ||
845 name == SWFDEC_AS_STR_EMPTY) {
846 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 3));
847 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 2), obj);
848 name = "";
849 pobj = obj;
850 } else {
851 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 3), obj);
852 swfdec_as_object_get_variable_and_flags (obj, name, swfdec_as_stack_peek (cx, 2), NULL, &pobj);
854 } else {
855 if (SWFDEC_AS_VALUE_IS_STRING (val))
856 name = SWFDEC_AS_VALUE_GET_STRING (val);
857 else
858 name = "???";
859 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_peek (cx, 3));
860 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
862 swfdec_as_stack_pop (cx);
863 /* setup super to point to the right prototype */
864 if (SWFDEC_IS_AS_SUPER (obj)) {
865 super = swfdec_as_super_resolve_property (SWFDEC_AS_SUPER (obj), name);
866 } else if (cx->version > 6 && pobj != obj) {
867 super = pobj;
868 } else if (obj) {
869 super = obj->prototype;
870 } else {
871 super = NULL;
873 if (!swfdec_action_call (cx, n_args, super)) {
874 SWFDEC_WARNING ("no function named \"%s\" on object %s", name,
875 obj ? G_OBJECT_TYPE_NAME(obj) : "unknown");
879 static void
880 swfdec_action_pop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
882 swfdec_as_stack_pop (cx);
885 static void
886 swfdec_action_binary (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
888 double l, r;
890 r = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
891 l = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
892 switch (action) {
893 case SWFDEC_AS_ACTION_ADD:
894 l = l + r;
895 break;
896 case SWFDEC_AS_ACTION_SUBTRACT:
897 l = l - r;
898 break;
899 case SWFDEC_AS_ACTION_MULTIPLY:
900 l = l * r;
901 break;
902 case SWFDEC_AS_ACTION_DIVIDE:
903 if (cx->version < 5) {
904 if (r == 0) {
905 swfdec_as_stack_pop (cx);
906 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), SWFDEC_AS_STR__ERROR_);
907 return;
910 if (r == 0) {
911 if (l > 0)
912 l = INFINITY;
913 else if (l < 0)
914 l = -INFINITY;
915 else
916 l = NAN;
917 } else {
918 l = l / r;
920 break;
921 default:
922 g_assert_not_reached ();
923 break;
925 swfdec_as_stack_pop (cx);
926 swfdec_as_value_set_number (cx, swfdec_as_stack_peek (cx, 1), l);
929 static void
930 swfdec_action_add2_to_primitive (SwfdecAsValue *value)
932 SwfdecAsObject *object;
933 const char *name;
935 if (!SWFDEC_AS_VALUE_IS_OBJECT (value))
936 return;
937 object = SWFDEC_AS_VALUE_GET_OBJECT (value);
938 if (SWFDEC_IS_MOVIE (object))
939 return;
941 if (SWFDEC_IS_AS_DATE (object) && swfdec_gc_object_get_context (object)->version > 5)
942 name = SWFDEC_AS_STR_toString;
943 else
944 name = SWFDEC_AS_STR_valueOf;
945 swfdec_as_object_call (SWFDEC_AS_VALUE_GET_OBJECT (value), name, 0, NULL, value);
948 static void
949 swfdec_action_add2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
951 SwfdecAsValue *rval, *lval, rtmp, ltmp;
953 rval = swfdec_as_stack_peek (cx, 1);
954 lval = swfdec_as_stack_peek (cx, 2);
955 rtmp = *rval;
956 ltmp = *lval;
957 swfdec_action_add2_to_primitive (&rtmp);
958 if (!SWFDEC_AS_VALUE_IS_OBJECT (&rtmp) || SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&rtmp)))
959 rval = &rtmp;
960 swfdec_action_add2_to_primitive (&ltmp);
961 if (!SWFDEC_AS_VALUE_IS_OBJECT (&ltmp) || SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&ltmp)))
962 lval = &ltmp;
964 if (SWFDEC_AS_VALUE_IS_STRING (lval) || SWFDEC_AS_VALUE_IS_STRING (rval)) {
965 const char *lstr, *rstr;
966 lstr = swfdec_as_value_to_string (cx, lval);
967 rstr = swfdec_as_value_to_string (cx, rval);
968 lstr = swfdec_as_str_concat (cx, lstr, rstr);
969 swfdec_as_stack_pop (cx);
970 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), lstr);
971 } else {
972 double d, d2;
973 d = swfdec_as_value_to_number (cx, lval);
974 d2 = swfdec_as_value_to_number (cx, rval);
975 d += d2;
976 swfdec_as_stack_pop (cx);
977 swfdec_as_value_set_number (cx, swfdec_as_stack_peek (cx, 1), d);
981 static void
982 swfdec_action_new_comparison (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
984 SwfdecAsValue *lval, *rval;
985 double l, r;
987 rval = swfdec_as_stack_peek (cx, 1);
988 lval = swfdec_as_stack_peek (cx, 2);
990 /* swap if we do a greater comparison */
991 if (action == SWFDEC_AS_ACTION_GREATER) {
992 SwfdecAsValue *tmp = lval;
993 lval = rval;
994 rval = tmp;
996 /* comparison with object is always false */
997 swfdec_as_value_to_primitive (lval);
998 if (SWFDEC_AS_VALUE_IS_OBJECT (lval) &&
999 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (lval))) {
1000 swfdec_as_stack_pop (cx);
1001 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
1002 return;
1004 /* same for the rval */
1005 swfdec_as_value_to_primitive (rval);
1006 if (SWFDEC_AS_VALUE_IS_OBJECT (rval) &&
1007 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (rval))) {
1008 swfdec_as_stack_pop (cx);
1009 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
1010 return;
1012 /* movieclips are not objects, but they evaluate to NaN, so we can handle them here */
1013 if (SWFDEC_AS_VALUE_IS_OBJECT (rval) ||
1014 SWFDEC_AS_VALUE_IS_OBJECT (lval)) {
1015 swfdec_as_stack_pop (cx);
1016 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1017 return;
1019 /* if both are strings, compare strings */
1020 if (SWFDEC_AS_VALUE_IS_STRING (rval) &&
1021 SWFDEC_AS_VALUE_IS_STRING (lval)) {
1022 const char *ls = SWFDEC_AS_VALUE_GET_STRING (lval);
1023 const char *rs = SWFDEC_AS_VALUE_GET_STRING (rval);
1024 int cmp;
1025 if (ls == SWFDEC_AS_STR_EMPTY) {
1026 cmp = rs == SWFDEC_AS_STR_EMPTY ? 0 : 1;
1027 } else if (rs == SWFDEC_AS_STR_EMPTY) {
1028 cmp = -1;
1029 } else {
1030 cmp = strcmp (ls, rs);
1032 swfdec_as_stack_pop (cx);
1033 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cmp < 0);
1034 return;
1036 /* convert to numbers and compare those */
1037 l = swfdec_as_value_to_number (cx, lval);
1038 r = swfdec_as_value_to_number (cx, rval);
1039 swfdec_as_stack_pop (cx);
1040 /* NaN results in undefined */
1041 if (isnan (l) || isnan (r)) {
1042 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1043 return;
1045 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), l < r);
1048 static void
1049 swfdec_action_not (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1051 if (cx->version <= 4) {
1052 double d = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
1053 swfdec_as_value_set_number (cx, swfdec_as_stack_peek (cx, 1), d == 0 ? 1 : 0);
1054 } else {
1055 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1),
1056 !swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1)));
1060 static void
1061 swfdec_action_jump (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1063 gint16 offset;
1065 if (len != 2) {
1066 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2)", len);
1067 return;
1069 offset = data[0] | (data[1] << 8);
1070 cx->frame->pc += 5 + (int) offset;
1073 static void
1074 swfdec_action_if (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1076 if (len != 2) {
1077 SWFDEC_ERROR ("If action length invalid (is %u, should be 2)", len);
1078 return;
1080 if (swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1))) {
1081 gint16 offset = data[0] | (data[1] << 8);
1082 cx->frame->pc += 5 + (int) offset;
1084 swfdec_as_stack_pop (cx);
1087 static void
1088 swfdec_action_decrement (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1090 SwfdecAsValue *val;
1092 val = swfdec_as_stack_peek (cx, 1);
1093 swfdec_as_value_set_number (cx, val, swfdec_as_value_to_number (cx, val) - 1);
1096 static void
1097 swfdec_action_increment (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1099 SwfdecAsValue *val;
1101 val = swfdec_as_stack_peek (cx, 1);
1102 swfdec_as_value_set_number (cx, val, swfdec_as_value_to_number (cx, val) + 1);
1105 static void
1106 swfdec_action_get_url (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1108 SwfdecBits bits;
1109 char *url, *target;
1111 swfdec_bits_init_data (&bits, data, len);
1112 url = swfdec_bits_get_string (&bits, cx->version);
1113 target = swfdec_bits_get_string (&bits, cx->version);
1114 if (url == NULL || target == NULL) {
1115 SWFDEC_ERROR ("not enough data in GetURL");
1116 g_free (url);
1117 g_free (target);
1118 return;
1120 if (swfdec_bits_left (&bits)) {
1121 SWFDEC_WARNING ("leftover bytes in GetURL action");
1123 if (!SWFDEC_IS_PLAYER (cx)) {
1124 SWFDEC_ERROR ("GetURL without a SwfdecPlayer");
1125 } else {
1126 swfdec_resource_load (SWFDEC_PLAYER (cx), target, url, NULL);
1128 g_free (url);
1129 g_free (target);
1132 static void
1133 swfdec_as_interpret_load_variables_on_finish (SwfdecAsObject *target,
1134 const char *text)
1136 if (text != NULL)
1137 swfdec_as_object_decode (target, text);
1139 // only call onData for sprite movies
1140 // FIXME: is it called even when loading fails?
1141 swfdec_actor_queue_script (SWFDEC_ACTOR (target), SWFDEC_EVENT_DATA);
1144 static gboolean
1145 swfdec_as_interpret_encode_variables_foreach (SwfdecAsObject *object,
1146 const char *variable, SwfdecAsValue *value, guint flags, gpointer data)
1148 SwfdecAsContext *context;
1149 GString *variables = data;
1150 char *escaped;
1152 context = swfdec_gc_object_get_context (object);
1153 // FIXME: check propflags?
1155 if (variables->len > 0)
1156 g_string_append_c (variables, '&');
1158 escaped = swfdec_as_string_escape (context, variable);
1159 g_string_append (variables, escaped);
1160 g_free (escaped);
1162 g_string_append_c (variables, '=');
1164 escaped = swfdec_as_string_escape (context,
1165 swfdec_as_value_to_string (context, value));
1166 g_string_append (variables, escaped);
1167 g_free (escaped);
1169 return TRUE;
1172 static char *
1173 swfdec_as_interpret_encode_variables (SwfdecAsObject *object)
1175 GString *variables = g_string_new ("");
1177 SWFDEC_FIXME ("Encoding variables for getURL2 shouldn't include child movies");
1178 swfdec_as_object_foreach (object,
1179 swfdec_as_interpret_encode_variables_foreach, variables);
1181 return g_string_free (variables, FALSE);
1184 static void
1185 swfdec_action_get_url2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1187 const char *target, *url;
1188 guint method, internal, variables;
1189 SwfdecBuffer *buffer;
1191 if (len != 1) {
1192 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
1193 return;
1196 method = data[0] & 3;
1197 if (method == 3) {
1198 SWFDEC_ERROR ("GetURL method 3 invalid");
1199 method = 0;
1201 internal = data[0] & 64;
1202 variables = data[0] & 128;
1204 url = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1205 buffer = NULL;
1207 if (method == 1 || method == 2) {
1208 char *text = swfdec_as_interpret_encode_variables (cx->frame->target);
1209 if (method == 1) {
1210 url = swfdec_as_context_give_string (cx, g_strjoin (NULL, url,
1211 strchr (url, '?') == NULL ? "?" : "&", text, NULL));
1212 } else {
1213 // don't send the nul-byte
1214 buffer = swfdec_buffer_new_for_data (g_memdup (text, strlen (text)),
1215 strlen (text));
1217 g_free (text);
1220 if (!SWFDEC_IS_PLAYER (cx)) {
1221 SWFDEC_ERROR ("GetURL2 action requires a SwfdecPlayer");
1222 } else if (variables) {
1223 SwfdecMovie *movie;
1225 target = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1226 movie = swfdec_player_get_movie_from_string (SWFDEC_PLAYER (cx), target);
1227 if (movie != NULL) {
1228 swfdec_load_object_create (SWFDEC_AS_OBJECT (movie), url, buffer, 0,
1229 NULL, NULL, NULL, swfdec_as_interpret_load_variables_on_finish);
1231 } else if (internal) {
1232 swfdec_resource_load_movie (SWFDEC_PLAYER (cx), swfdec_as_stack_peek (cx, 1),
1233 url, NULL, NULL);
1234 } else {
1235 target = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1236 swfdec_resource_load (SWFDEC_PLAYER (cx), target, url, buffer);
1239 swfdec_as_stack_pop_n (cx, 2);
1242 static void
1243 swfdec_action_string_add (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1245 const char *lval, *rval;
1247 rval = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1248 lval = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1249 lval = swfdec_as_str_concat (cx, lval, rval);
1250 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 2), lval);
1251 swfdec_as_stack_pop (cx);
1254 static void
1255 swfdec_action_push_duplicate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1257 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
1259 *swfdec_as_stack_push (cx) = *val;
1262 static void
1263 swfdec_action_random_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1265 gint32 max;
1266 SwfdecAsValue *val;
1268 val = swfdec_as_stack_peek (cx, 1);
1269 max = swfdec_as_value_to_integer (cx, val);
1271 if (max <= 0)
1272 swfdec_as_value_set_number (cx, val, 0);
1273 else
1274 swfdec_as_value_set_number (cx, val, g_rand_int_range (cx->rand, 0, max));
1277 static void
1278 swfdec_action_old_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1280 double l, r;
1281 gboolean cond;
1283 l = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
1284 r = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
1285 switch (action) {
1286 case SWFDEC_AS_ACTION_EQUALS:
1287 cond = l == r;
1288 break;
1289 case SWFDEC_AS_ACTION_LESS:
1290 cond = l < r;
1291 break;
1292 default:
1293 g_assert_not_reached ();
1294 return;
1296 swfdec_as_stack_pop (cx);
1297 if (cx->version < 5) {
1298 swfdec_as_value_set_number (cx, swfdec_as_stack_peek (cx, 1), cond ? 1 : 0);
1299 } else {
1300 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1304 static void
1305 swfdec_action_string_extract (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1307 int start, n, left;
1308 const char *s;
1310 n = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1311 start = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1312 s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 3));
1313 swfdec_as_stack_pop_n (cx, 2);
1314 left = g_utf8_strlen (s, -1);
1315 if (start > left) {
1316 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), SWFDEC_AS_STR_EMPTY);
1317 return;
1318 } else if (start < 2) {
1319 start = 0;
1320 } else {
1321 start--;
1323 left -= start;
1324 if (n < 0 || n > left)
1325 n = left;
1327 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1),
1328 swfdec_as_str_sub (cx, s, start, n));
1331 static void
1332 swfdec_action_string_length (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1334 const char *s;
1335 SwfdecAsValue *v;
1337 v = swfdec_as_stack_peek (cx, 1);
1338 s = swfdec_as_value_to_string (cx, v);
1339 swfdec_as_value_set_integer (cx, v, g_utf8_strlen (s, -1));
1342 static void
1343 swfdec_action_string_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1345 const char *l, *r;
1346 gboolean cond;
1348 r = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1349 l = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1350 switch (action) {
1351 case SWFDEC_AS_ACTION_STRING_EQUALS:
1352 cond = l == r;
1353 break;
1354 case SWFDEC_AS_ACTION_STRING_LESS:
1355 cond = strcmp (l, r) < 0;
1356 break;
1357 case SWFDEC_AS_ACTION_STRING_GREATER:
1358 cond = strcmp (l, r) > 0;
1359 break;
1360 default:
1361 cond = FALSE;
1362 g_assert_not_reached ();
1363 break;
1365 swfdec_as_stack_pop (cx);
1366 if (cx->version < 5) {
1367 swfdec_as_value_set_number (cx, swfdec_as_stack_peek (cx, 1), cond ? 1 : 0);
1368 } else {
1369 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1373 static void
1374 swfdec_action_equals2_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1376 SwfdecAsValue *rval, *lval;
1377 SwfdecAsValue rtmp, ltmp;
1378 SwfdecAsValueType ltype, rtype;
1379 double l, r;
1380 gboolean cond;
1382 rval = swfdec_as_stack_peek (cx, 1);
1383 lval = swfdec_as_stack_peek (cx, 2);
1384 rtmp = *rval;
1385 ltmp = *lval;
1386 swfdec_as_value_to_primitive (&rtmp);
1387 swfdec_as_value_to_primitive (&ltmp);
1388 ltype = ltmp.type;
1389 rtype = rtmp.type;
1391 /* get objects compared */
1392 if (ltype == SWFDEC_AS_TYPE_OBJECT && rtype == SWFDEC_AS_TYPE_OBJECT) {
1393 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (&ltmp);
1394 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (&rtmp);
1396 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1397 lo = SWFDEC_AS_OBJECT (swfdec_movie_resolve (SWFDEC_MOVIE (lo)));
1398 ro = SWFDEC_AS_OBJECT (swfdec_movie_resolve (SWFDEC_MOVIE (ro)));
1399 } else if (SWFDEC_IS_MOVIE (lo)) {
1400 swfdec_as_value_to_primitive (rval);
1401 rtype = rval->type;
1402 if (rtype != SWFDEC_AS_TYPE_OBJECT) {
1403 cond = FALSE;
1404 goto out;
1406 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1407 } else if (SWFDEC_IS_MOVIE (ro)) {
1408 swfdec_as_value_to_primitive (lval);
1409 ltype = lval->type;
1410 if (ltype != SWFDEC_AS_TYPE_OBJECT) {
1411 cond = FALSE;
1412 goto out;
1414 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1415 } else {
1416 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1417 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1419 cond = lo == ro;
1420 goto out;
1423 /* compare strings */
1424 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1425 /* FIXME: flash 5 case insensitive? */
1426 cond = SWFDEC_AS_VALUE_GET_STRING (&ltmp) == SWFDEC_AS_VALUE_GET_STRING (&rtmp);
1427 goto out;
1430 /* convert to numbers */
1431 if (SWFDEC_AS_VALUE_IS_OBJECT (&ltmp) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&ltmp))) {
1432 l = swfdec_as_value_to_number (cx, lval);
1433 } else {
1434 l = swfdec_as_value_to_number (cx, &ltmp);
1436 if (SWFDEC_AS_VALUE_IS_OBJECT (&rtmp) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&rtmp))) {
1437 r = swfdec_as_value_to_number (cx, rval);
1438 } else {
1439 r = swfdec_as_value_to_number (cx, &rtmp);
1442 /* get rid of undefined and null */
1443 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1444 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1445 goto out;
1446 } else if (cond) {
1447 cond = FALSE;
1448 goto out;
1451 /* else compare as numbers */
1452 if (isnan (l) && isnan (r))
1453 cond = ltype == rtype;
1454 else
1455 cond = l == r;
1457 out:
1458 swfdec_as_stack_pop (cx);
1459 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1462 static void
1463 swfdec_action_equals2_6 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1465 SwfdecAsValue *rval, *lval;
1466 SwfdecAsValueType ltype, rtype;
1467 double l, r;
1468 gboolean cond;
1470 rval = swfdec_as_stack_peek (cx, 1);
1471 lval = swfdec_as_stack_peek (cx, 2);
1472 ltype = lval->type;
1473 rtype = rval->type;
1475 /* get objects compared */
1476 if (ltype == SWFDEC_AS_TYPE_OBJECT && rtype == SWFDEC_AS_TYPE_OBJECT) {
1477 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1478 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1480 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1481 lo = SWFDEC_AS_OBJECT (swfdec_movie_resolve (SWFDEC_MOVIE (lo)));
1482 ro = SWFDEC_AS_OBJECT (swfdec_movie_resolve (SWFDEC_MOVIE (ro)));
1483 } else if (SWFDEC_IS_MOVIE (lo)) {
1484 swfdec_as_value_to_primitive (rval);
1485 rtype = rval->type;
1486 if (rtype != SWFDEC_AS_TYPE_OBJECT) {
1487 cond = FALSE;
1488 goto out;
1490 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1491 } else if (SWFDEC_IS_MOVIE (ro)) {
1492 swfdec_as_value_to_primitive (lval);
1493 ltype = lval->type;
1494 if (ltype != SWFDEC_AS_TYPE_OBJECT) {
1495 cond = FALSE;
1496 goto out;
1498 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1500 cond = lo == ro;
1501 goto out;
1504 /* if one of the values is an object, call valueOf.
1505 * If it's still an object, return FALSE */
1506 swfdec_as_value_to_primitive (lval);
1507 ltype = lval->type;
1508 if (ltype == SWFDEC_AS_TYPE_OBJECT) {
1509 cond = FALSE;
1510 goto out;
1512 swfdec_as_value_to_primitive (rval);
1513 rtype = rval->type;
1514 if (rtype == SWFDEC_AS_TYPE_OBJECT) {
1515 cond = FALSE;
1516 goto out;
1518 /* now we have a comparison without objects */
1520 /* get rid of undefined and null */
1521 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1522 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1523 goto out;
1524 } else if (cond) {
1525 cond = FALSE;
1526 goto out;
1529 /* compare strings */
1530 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1531 /* FIXME: flash 5 case insensitive? */
1532 cond = SWFDEC_AS_VALUE_GET_STRING (lval) == SWFDEC_AS_VALUE_GET_STRING (rval);
1533 goto out;
1536 /* else compare as numbers */
1537 l = swfdec_as_value_to_number (cx, lval);
1538 r = swfdec_as_value_to_number (cx, rval);
1540 if (isnan (l) && isnan (r))
1541 cond = ltype == rtype;
1542 else
1543 cond = l == r;
1545 out:
1546 swfdec_as_stack_pop (cx);
1547 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1550 static void
1551 swfdec_action_equals2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1553 if (cx->version <= 5) {
1554 swfdec_action_equals2_5 (cx, action, data, len);
1555 } else {
1556 swfdec_action_equals2_6 (cx, action, data, len);
1560 static void
1561 swfdec_action_strict_equals (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1563 SwfdecAsValue *rval, *lval;
1564 gboolean cond;
1566 rval = swfdec_as_stack_peek (cx, 1);
1567 lval = swfdec_as_stack_peek (cx, 2);
1569 if (rval->type != lval->type) {
1570 cond = FALSE;
1571 } else {
1572 switch (rval->type) {
1573 case SWFDEC_AS_TYPE_UNDEFINED:
1574 case SWFDEC_AS_TYPE_NULL:
1575 cond = TRUE;
1576 break;
1577 case SWFDEC_AS_TYPE_BOOLEAN:
1578 cond = SWFDEC_AS_VALUE_GET_BOOLEAN (rval) == SWFDEC_AS_VALUE_GET_BOOLEAN (lval);
1579 break;
1580 case SWFDEC_AS_TYPE_NUMBER:
1582 double l, r;
1583 r = SWFDEC_AS_VALUE_GET_NUMBER (rval);
1584 l = SWFDEC_AS_VALUE_GET_NUMBER (lval);
1585 cond = (l == r) || (isnan (l) && isnan (r));
1587 break;
1588 case SWFDEC_AS_TYPE_STRING:
1589 cond = SWFDEC_AS_VALUE_GET_STRING (rval) == SWFDEC_AS_VALUE_GET_STRING (lval);
1590 break;
1591 case SWFDEC_AS_TYPE_OBJECT:
1593 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1594 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1595 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1596 cond = swfdec_movie_resolve (SWFDEC_MOVIE (lo)) == swfdec_movie_resolve (SWFDEC_MOVIE (ro));
1597 } else if (!SWFDEC_IS_MOVIE (lo) && !SWFDEC_IS_MOVIE (ro)) {
1598 cond = lo == ro;
1599 } else {
1600 cond = FALSE;
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 (!SWFDEC_IS_MOVIE (o)) {
1625 SWFDEC_FIXME ("target \"%s\" is not a movie, something weird is supposed to happen now", target);
1626 o = NULL;
1628 swfdec_as_frame_set_target (cx->frame, o);
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))) {
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_OBJECT (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_OBJECT (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))) {
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);
1790 for (i = 0; i < n_args; i++) {
1791 const char *s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1792 swfdec_as_object_set_variable (object, s, swfdec_as_stack_peek (cx, 1));
1793 swfdec_as_stack_pop_n (cx, 2);
1795 swfdec_as_stack_pop_n (cx, size);
1796 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), object);
1799 static void
1800 swfdec_action_init_array (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1802 int i, n;
1803 SwfdecAsObject *array;
1805 swfdec_as_stack_ensure_size (cx, 1);
1806 n = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1807 swfdec_as_stack_pop (cx);
1808 array = swfdec_as_array_new (cx);
1809 /* NB: we can't increase the stack here, as the number can easily be MAXINT */
1810 for (i = 0; i < n && swfdec_as_stack_get_size (cx) > 0; i++) {
1811 swfdec_as_stack_ensure_size (cx, 1);
1812 swfdec_as_array_push (SWFDEC_AS_ARRAY (array), swfdec_as_stack_pop (cx));
1814 if (i != n) {
1815 SwfdecAsValue val;
1816 swfdec_as_value_set_integer (cx, &val, n);
1817 swfdec_as_object_set_variable (array, SWFDEC_AS_STR_length, &val);
1819 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), array);
1822 static void
1823 swfdec_action_define_function (SwfdecAsContext *cx, guint action,
1824 const guint8 *data, guint len)
1826 char *function_name;
1827 const char *name = NULL;
1828 guint i, n_args, size, n_registers;
1829 SwfdecBits bits;
1830 SwfdecBuffer *buffer;
1831 SwfdecAsFunction *fun;
1832 SwfdecAsFrame *frame;
1833 SwfdecScript *script;
1834 guint flags = 0;
1835 SwfdecScriptArgument *args;
1836 gboolean v2 = (action == 0x8e);
1838 frame = cx->frame;
1839 swfdec_bits_init_data (&bits, data, len);
1840 function_name = swfdec_bits_get_string (&bits, cx->version);
1841 if (function_name == NULL) {
1842 SWFDEC_ERROR ("could not parse function name");
1843 return;
1845 n_args = swfdec_bits_get_u16 (&bits);
1846 if (v2) {
1847 n_registers = swfdec_bits_get_u8 (&bits);
1848 if (n_registers == 0)
1849 n_registers = 4;
1850 flags = swfdec_bits_get_u16 (&bits);
1851 } else {
1852 n_registers = 5;
1854 if (n_args) {
1855 args = g_new0 (SwfdecScriptArgument, n_args);
1856 for (i = 0; i < n_args && swfdec_bits_left (&bits); i++) {
1857 if (v2) {
1858 args[i].preload = swfdec_bits_get_u8 (&bits);
1859 if (args[i].preload && args[i].preload >= n_registers) {
1860 SWFDEC_ERROR ("argument %u cannot be preloaded into register %u out of %u",
1861 i, args[i].preload, n_registers);
1862 /* FIXME: figure out correct error handling here */
1863 args[i].preload = 0;
1866 args[i].name = swfdec_bits_get_string (&bits, cx->version);
1867 if (args[i].name == NULL || args[i].name == '\0') {
1868 SWFDEC_ERROR ("empty argument name not allowed");
1869 g_free (args);
1870 return;
1872 /* FIXME: check duplicate arguments */
1874 } else {
1875 args = NULL;
1877 size = swfdec_bits_get_u16 (&bits);
1878 /* check the script can be created */
1879 if (frame->script->buffer->data + frame->script->buffer->length < frame->pc + 3 + len + size) {
1880 SWFDEC_ERROR ("size of function is too big");
1881 g_free (args);
1882 g_free (function_name);
1883 return;
1885 /* create the script */
1886 buffer = swfdec_buffer_new_subbuffer (frame->script->buffer,
1887 frame->pc + 3 + len - frame->script->buffer->data, size);
1888 swfdec_bits_init (&bits, buffer);
1889 if (*function_name) {
1890 name = function_name;
1891 } else if (swfdec_as_stack_get_size (cx) > 0) {
1892 /* This is kind of a hack that uses a feature of the Adobe compiler:
1893 * foo = function () {} is compiled as these actions:
1894 * Push "foo", DefineFunction, SetVariable/SetMember
1895 * With this knowledge we can inspect the topmost stack member, since
1896 * it will contain the name this function will soon be assigned to.
1898 if (SWFDEC_AS_VALUE_IS_STRING (swfdec_as_stack_peek (cx, 1)))
1899 name = SWFDEC_AS_VALUE_GET_STRING (swfdec_as_stack_peek (cx, 1));
1901 if (name == NULL)
1902 name = "unnamed_function";
1903 script = swfdec_script_new_from_bits (&bits, name, cx->version);
1904 swfdec_buffer_unref (buffer);
1905 if (script == NULL) {
1906 SWFDEC_ERROR ("failed to create script");
1907 g_free (args);
1908 g_free (function_name);
1909 return;
1911 if (frame->constant_pool)
1912 script->constant_pool = swfdec_buffer_ref (swfdec_constant_pool_get_buffer (frame->constant_pool));
1913 script->flags = flags;
1914 script->n_registers = n_registers;
1915 script->n_arguments = n_args;
1916 script->arguments = args;
1917 /* see function-scope tests */
1918 if (cx->version > 5) {
1919 /* FIXME: or original target? */
1920 fun = swfdec_as_script_function_new (frame->original_target, frame->scope_chain, script);
1921 } else {
1922 fun = swfdec_as_script_function_new (frame->original_target, NULL, script);
1924 if (fun == NULL)
1925 return;
1926 /* This is a hack that should only trigger for functions defined in the init scripts.
1927 * It is supposed to ensure that those functions inherit their target when being
1928 * called instead of when being defined */
1929 if (!SWFDEC_IS_MOVIE (frame->original_target))
1930 SWFDEC_AS_SCRIPT_FUNCTION (fun)->target = NULL;
1931 /* attach the function */
1932 if (*function_name == '\0') {
1933 swfdec_as_stack_ensure_free (cx, 1);
1934 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), SWFDEC_AS_OBJECT (fun));
1935 } else {
1936 SwfdecAsValue funval;
1937 /* FIXME: really varobj? Not eval or sth like that? */
1938 name = swfdec_as_context_get_string (cx, function_name);
1939 SWFDEC_AS_VALUE_SET_OBJECT (&funval, SWFDEC_AS_OBJECT (fun));
1940 swfdec_as_object_set_variable (frame->target, name, &funval);
1943 /* update current context */
1944 frame->pc += 3 + len + size;
1945 g_free (function_name);
1948 static void
1949 swfdec_action_bitwise (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1951 int a, b;
1953 a = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1954 b = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1956 switch (action) {
1957 case 0x60:
1958 a = (int) (a & b);
1959 break;
1960 case 0x61:
1961 a = (int) (a | b);
1962 break;
1963 case 0x62:
1964 a = (int) (a ^ b);
1965 break;
1966 default:
1967 g_assert_not_reached ();
1968 break;
1971 swfdec_as_stack_pop (cx);
1972 swfdec_as_value_set_integer (cx, swfdec_as_stack_peek (cx, 1), a);
1975 static void
1976 swfdec_action_shift (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1978 int amount, value;
1980 amount = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1981 amount &= 31;
1982 value = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1984 switch (action) {
1985 case 0x63:
1986 value = value << amount;
1987 break;
1988 case 0x64:
1989 value = ((gint) value) >> amount;
1990 break;
1991 case 0x65:
1992 value = ((guint) value) >> amount;
1993 break;
1994 default:
1995 g_assert_not_reached ();
1998 swfdec_as_stack_pop (cx);
1999 swfdec_as_value_set_integer (cx, swfdec_as_stack_peek (cx, 1), value);
2002 static void
2003 swfdec_action_to_integer (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2005 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2007 swfdec_as_value_set_integer (cx, val, swfdec_as_value_to_integer (cx, val));
2010 static void
2011 swfdec_action_target_path (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2013 SwfdecAsValue *val;
2014 SwfdecMovie *movie;
2015 char *s;
2017 val = swfdec_as_stack_peek (cx, 1);
2019 if (!SWFDEC_AS_VALUE_IS_OBJECT (val) ||
2020 !SWFDEC_IS_MOVIE (movie = (SwfdecMovie *) SWFDEC_AS_VALUE_GET_OBJECT (val))) {
2021 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
2022 return;
2024 s = swfdec_movie_get_path (movie, TRUE);
2025 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_give_string (cx, s));
2028 static void
2029 swfdec_action_define_local (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2031 const char *name;
2033 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
2034 swfdec_as_frame_set_variable (cx->frame, name, swfdec_as_stack_peek (cx, 1),
2035 TRUE, TRUE);
2036 swfdec_as_stack_pop_n (cx, 2);
2039 static void
2040 swfdec_action_define_local2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2042 SwfdecAsValue val = { 0, };
2043 const char *name;
2045 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
2046 swfdec_as_frame_set_variable (cx->frame, name, &val, FALSE, TRUE);
2047 swfdec_as_stack_pop (cx);
2050 static void
2051 swfdec_action_end (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2053 swfdec_as_frame_return (cx->frame, NULL);
2056 static void
2057 swfdec_action_return (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2059 swfdec_as_frame_return (cx->frame, swfdec_as_stack_pop (cx));
2062 static void
2063 swfdec_action_delete (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2065 SwfdecAsValue *val;
2066 const char *name;
2067 gboolean success = FALSE;
2069 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
2070 val = swfdec_as_stack_peek (cx, 2);
2071 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2072 success = swfdec_as_object_delete_variable (
2073 SWFDEC_AS_VALUE_GET_OBJECT (val), name) == SWFDEC_AS_DELETE_DELETED;
2075 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
2076 swfdec_as_stack_pop_n (cx, 1);
2079 static void
2080 swfdec_action_delete2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2082 SwfdecAsValue *val;
2083 const char *name;
2084 gboolean success = FALSE;
2086 val = swfdec_as_stack_peek (cx, 1);
2087 name = swfdec_as_value_to_string (cx, val);
2088 success = swfdec_as_frame_delete_variable (cx->frame, name) == SWFDEC_AS_DELETE_DELETED;
2089 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
2092 static void
2093 swfdec_action_store_register (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2095 if (len != 1) {
2096 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
2097 return;
2099 if (!swfdec_action_has_register (cx, *data)) {
2100 SWFDEC_ERROR ("Cannot store into register %u, not enough registers", (guint) *data);
2101 return;
2103 cx->frame->registers[*data] = *swfdec_as_stack_peek (cx, 1);
2106 static void
2107 swfdec_action_modulo (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2109 double x, y;
2111 y = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
2112 x = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
2113 /* yay, we're portable! */
2114 if (y == 0.0) {
2115 x = NAN;
2116 } else {
2117 errno = 0;
2118 x = fmod (x, y);
2119 if (errno != 0) {
2120 SWFDEC_FIXME ("errno set after fmod");
2123 swfdec_as_stack_pop (cx);
2124 swfdec_as_value_set_number (cx, swfdec_as_stack_peek (cx, 1), x);
2127 static void
2128 swfdec_action_swap (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2130 swfdec_as_stack_swap (cx, 1, 2);
2133 static void
2134 swfdec_action_to_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2136 swfdec_as_value_set_number (cx, swfdec_as_stack_peek (cx, 1),
2137 swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1)));
2140 static void
2141 swfdec_action_to_string (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2143 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1),
2144 swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1)));
2147 static void
2148 swfdec_action_type_of (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2150 SwfdecAsValue *val;
2151 const char *type;
2153 val = swfdec_as_stack_peek (cx, 1);
2154 switch (val->type) {
2155 case SWFDEC_AS_TYPE_NUMBER:
2156 type = SWFDEC_AS_STR_number;
2157 break;
2158 case SWFDEC_AS_TYPE_BOOLEAN:
2159 type = SWFDEC_AS_STR_boolean;
2160 break;
2161 case SWFDEC_AS_TYPE_STRING:
2162 type = SWFDEC_AS_STR_string;
2163 break;
2164 case SWFDEC_AS_TYPE_UNDEFINED:
2165 type = SWFDEC_AS_STR_undefined;
2166 break;
2167 case SWFDEC_AS_TYPE_NULL:
2168 type = SWFDEC_AS_STR_null;
2169 break;
2170 case SWFDEC_AS_TYPE_OBJECT:
2172 SwfdecAsObject *obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
2173 if (SWFDEC_IS_MOVIE (obj)) {
2174 SwfdecMovie *movie = swfdec_movie_resolve (SWFDEC_MOVIE (obj));
2175 if (movie != NULL && SWFDEC_IS_TEXT_FIELD_MOVIE (movie) &&
2176 movie->state == SWFDEC_MOVIE_STATE_RUNNING) {
2177 type = SWFDEC_AS_STR_object;
2178 } else {
2179 type = SWFDEC_AS_STR_movieclip;
2181 } else if (SWFDEC_IS_AS_FUNCTION (obj)) {
2182 type = SWFDEC_AS_STR_function;
2183 } else {
2184 type = SWFDEC_AS_STR_object;
2187 break;
2188 case SWFDEC_AS_TYPE_INT:
2189 default:
2190 g_assert_not_reached ();
2191 type = SWFDEC_AS_STR_EMPTY;
2192 break;
2194 SWFDEC_AS_VALUE_SET_STRING (val, type);
2197 static void
2198 swfdec_action_get_time (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2200 GTimeVal tv;
2201 double diff;
2203 swfdec_as_context_get_time (cx, &tv);
2204 /* we assume here that swfdec_as_context_get_time always returns a tv > start_time */
2205 diff = tv.tv_sec - cx->start_time.tv_sec;
2206 diff *= 1000;
2207 diff += (tv.tv_usec - cx->start_time.tv_usec) / 1000;
2209 swfdec_as_value_set_number (cx, swfdec_as_stack_push (cx), diff);
2212 static gboolean
2213 swfdec_action_is_instance_of (SwfdecAsObject *object,
2214 SwfdecAsObject *constructor)
2216 SwfdecAsValue val;
2217 SwfdecAsObject *class, *prototype;
2218 GSList *iter;
2220 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (object), FALSE);
2221 g_return_val_if_fail (SWFDEC_IS_AS_OBJECT (constructor), FALSE);
2223 // FIXME: propflag tests are wrong, and we shouldn't get __proto__.prototype
2224 swfdec_as_object_get_variable (constructor, SWFDEC_AS_STR_prototype, &val);
2225 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val))
2226 return FALSE;
2227 prototype = SWFDEC_AS_VALUE_GET_OBJECT (&val);
2229 class = object;
2230 while ((class = swfdec_as_object_get_prototype (class)) != NULL) {
2231 if (class == prototype)
2232 return TRUE;
2233 for (iter = class->interfaces; iter != NULL; iter = iter->next) {
2234 if (iter->data == prototype)
2235 return TRUE;
2239 return FALSE;
2242 static void
2243 swfdec_action_instance_of (SwfdecAsContext *cx, guint action,
2244 const guint8 *data, guint len)
2246 SwfdecAsValue *val;
2247 SwfdecAsObject *object, *constructor;
2249 val = swfdec_as_stack_pop (cx);
2250 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2251 constructor = SWFDEC_AS_VALUE_GET_OBJECT (val);
2252 } else {
2253 constructor = NULL;
2256 val = swfdec_as_stack_pop (cx);
2257 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2258 object = SWFDEC_AS_VALUE_GET_OBJECT (val);
2259 } else {
2260 object = NULL;
2264 if (object == NULL || constructor == NULL) {
2265 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx), FALSE);
2266 return;
2269 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx),
2270 swfdec_action_is_instance_of (object, constructor));
2273 static void
2274 swfdec_action_cast (SwfdecAsContext *cx, guint action, const guint8 *data,
2275 guint len)
2277 SwfdecAsValue *val;
2278 SwfdecAsObject *object, *constructor;
2280 val = swfdec_as_stack_pop (cx);
2281 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2282 object = SWFDEC_AS_VALUE_GET_OBJECT (val);
2283 } else {
2284 object = NULL;
2287 val = swfdec_as_stack_pop (cx);
2288 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2289 constructor = SWFDEC_AS_VALUE_GET_OBJECT (val);
2290 } else {
2291 constructor = NULL;
2294 if (object == NULL || constructor == NULL) {
2295 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
2296 return;
2299 if (swfdec_action_is_instance_of (object, constructor)) {
2300 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), object);
2301 } else {
2302 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
2306 static void
2307 swfdec_action_implements (SwfdecAsContext *cx, guint action,
2308 const guint8 *data, guint len)
2310 SwfdecAsValue *val, *argv;
2311 SwfdecAsObject *object, *proto, *interface;
2312 int argc, i;
2314 swfdec_as_stack_ensure_size (cx, 2);
2316 val = swfdec_as_stack_pop (cx);
2317 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2318 object = SWFDEC_AS_VALUE_GET_OBJECT (val);
2319 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_prototype, val);
2320 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2321 proto = SWFDEC_AS_VALUE_GET_OBJECT (val);
2322 } else {
2323 proto = NULL;
2325 } else {
2326 object = NULL;
2327 proto = NULL;
2330 val = swfdec_as_stack_pop (cx);
2331 argc = swfdec_as_value_to_integer (cx, val);
2333 if (argc > 0) {
2334 swfdec_as_stack_ensure_size (cx, argc);
2335 argv = swfdec_as_stack_pop_n (cx, argc);
2336 } else {
2337 argv = NULL;
2340 if (proto == NULL)
2341 return;
2343 for (i = 0; i < argc; i++) {
2344 if (!SWFDEC_AS_VALUE_IS_OBJECT (&argv[i]))
2345 continue;
2346 interface = SWFDEC_AS_VALUE_GET_OBJECT (&argv[i]);
2347 swfdec_as_object_get_variable (interface, SWFDEC_AS_STR_prototype, val);
2348 if (!SWFDEC_AS_VALUE_IS_OBJECT (val))
2349 continue;
2350 proto->interfaces =
2351 g_slist_prepend (proto->interfaces, SWFDEC_AS_VALUE_GET_OBJECT (val));
2355 static void
2356 swfdec_action_extends (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2358 SwfdecAsValue *superclass, *subclass, proto;
2359 SwfdecAsObject *prototype;
2360 SwfdecAsObject *super;
2362 superclass = swfdec_as_stack_peek (cx, 1);
2363 subclass = swfdec_as_stack_peek (cx, 2);
2364 if (!SWFDEC_AS_VALUE_IS_OBJECT (superclass) ||
2365 !SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (superclass))) {
2366 SWFDEC_ERROR ("superclass is not a function");
2367 goto fail;
2369 if (!SWFDEC_AS_VALUE_IS_OBJECT (subclass)) {
2370 SWFDEC_ERROR ("subclass is not an object");
2371 goto fail;
2373 super = SWFDEC_AS_VALUE_GET_OBJECT (superclass);
2374 prototype = swfdec_as_object_new_empty (cx);
2375 swfdec_as_object_get_variable (super, SWFDEC_AS_STR_prototype, &proto);
2376 swfdec_as_object_set_variable_and_flags (prototype, SWFDEC_AS_STR___proto__, &proto,
2377 SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
2378 swfdec_as_object_set_variable_and_flags (prototype, SWFDEC_AS_STR___constructor__,
2379 superclass, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_VERSION_6_UP);
2380 SWFDEC_AS_VALUE_SET_OBJECT (&proto, prototype);
2381 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_OBJECT (subclass),
2382 SWFDEC_AS_STR_prototype, &proto);
2383 fail:
2384 swfdec_as_stack_pop_n (cx, 2);
2387 static gboolean
2388 swfdec_action_enumerate_foreach (SwfdecAsObject *object, const char *variable,
2389 SwfdecAsValue *value, guint flags, gpointer listp)
2391 GSList **list = listp;
2393 if (flags & SWFDEC_AS_VARIABLE_HIDDEN)
2394 return TRUE;
2396 *list = g_slist_remove (*list, variable);
2397 *list = g_slist_prepend (*list, (gpointer) variable);
2398 return TRUE;
2401 static void
2402 swfdec_action_do_enumerate (SwfdecAsContext *cx, SwfdecAsObject *object)
2404 guint i;
2405 GSList *walk, *list = NULL;
2407 for (i = 0; i < 256 && object; i++) {
2408 swfdec_as_object_foreach (object, swfdec_action_enumerate_foreach, &list);
2409 object = swfdec_as_object_get_prototype (object);
2411 if (i == 256) {
2412 swfdec_as_context_abort (swfdec_gc_object_get_context (object),
2413 "Prototype recursion limit exceeded");
2414 g_slist_free (list);
2415 return;
2417 list = g_slist_reverse (list);
2418 i = 0;
2419 for (walk = list; walk; walk = walk->next) {
2420 /* 8 is an arbitrary value */
2421 if (i % 8 == 0) {
2422 swfdec_as_stack_ensure_free (cx, 8);
2423 i = 0;
2425 i++;
2426 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx), walk->data);
2428 g_slist_free (list);
2431 static void
2432 swfdec_action_enumerate2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2434 SwfdecAsValue *val;
2435 SwfdecAsObject *obj;
2437 val = swfdec_as_stack_peek (cx, 1);
2438 if (!SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2439 SWFDEC_WARNING ("Enumerate called without an object");
2440 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
2441 return;
2443 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
2444 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
2445 swfdec_action_do_enumerate (cx, obj);
2448 static void
2449 swfdec_action_enumerate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2451 /* FIXME: make this proper functions */
2452 swfdec_action_get_variable (cx, action, data, len);
2453 swfdec_action_enumerate2 (cx, action, data, len);
2456 static void
2457 swfdec_action_logical (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2459 gboolean l, r;
2461 if (cx->version <= 4) {
2462 l = (swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1)) != 0);
2463 // don't call second parameter if not necessary
2464 if ((action == SWFDEC_AS_ACTION_AND && !l) ||
2465 (action != SWFDEC_AS_ACTION_AND && l)) {
2466 r = (swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2)) != 0);
2467 } else {
2468 r = FALSE;
2470 } else {
2471 l = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1));
2472 r = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 2));
2475 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 2),
2476 (action == SWFDEC_AS_ACTION_AND) ? (l && r) : (l || r));
2477 swfdec_as_stack_pop (cx);
2480 static void
2481 swfdec_action_char_to_ascii (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2483 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2484 const char *s = swfdec_as_value_to_string (cx, val);
2486 if (cx->version <= 5) {
2487 char *ascii = g_convert (s, -1, "LATIN1", "UTF-8", NULL, NULL, NULL);
2489 if (ascii == NULL) {
2490 /* This can happen if a Flash 5 movie gets loaded into a Flash 7 movie */
2491 SWFDEC_FIXME ("Someone threw unconvertible text %s at Flash <= 5", s);
2492 swfdec_as_value_set_integer (cx, val, 0); /* FIXME: what to return??? */
2493 } else {
2494 swfdec_as_value_set_integer (cx, val, (guchar) ascii[0]);
2495 g_free (ascii);
2497 } else {
2498 gunichar *uni = g_utf8_to_ucs4_fast (s, -1, NULL);
2500 if (uni == NULL) {
2501 /* This should never happen, everything is valid UTF-8 in here */
2502 g_warning ("conversion of character %s failed", s);
2503 swfdec_as_value_set_integer (cx, val, 0);
2504 } else {
2505 swfdec_as_value_set_integer (cx, val, uni[0]);
2506 g_free (uni);
2511 static void
2512 swfdec_action_ascii_to_char (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2514 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2516 if (cx->version <= 5) {
2517 char s[3];
2518 char *utf8;
2519 guint i;
2521 if (action == SWFDEC_AS_ACTION_ASCII_TO_CHAR) {
2522 s[0] = ((guint) swfdec_as_value_to_integer (cx, val)) % 256;
2523 s[1] = 0;
2524 } else {
2525 g_assert (action == SWFDEC_AS_ACTION_MB_ASCII_TO_CHAR);
2527 i = ((guint) swfdec_as_value_to_integer (cx, val));
2528 if (i > 255) {
2529 s[0] = i / 256;
2530 s[1] = i % 256;
2531 s[2] = 0;
2532 } else {
2533 s[0] = i;
2534 s[1] = 0;
2538 utf8 = g_convert (s, -1, "UTF-8", "LATIN1", NULL, NULL, NULL);
2539 if (utf8 == NULL) {
2540 g_warning ("conversion of character %u failed", (guint) s[0]);
2541 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2542 } else {
2543 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, utf8));
2544 g_free (utf8);
2546 } else {
2547 char *s;
2548 gunichar c = ((guint) swfdec_as_value_to_integer (cx, val)) % 65536;
2550 s = g_ucs4_to_utf8 (&c, 1, NULL, NULL, NULL);
2551 if (s == NULL) {
2552 g_warning ("conversion of character %u failed", (guint) c);
2553 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2554 } else {
2555 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, s));
2556 g_free (s);
2561 static void
2562 swfdec_action_throw (SwfdecAsContext *cx, guint action, const guint8 *data,
2563 guint len)
2565 swfdec_as_context_throw (cx, swfdec_as_stack_pop (cx));
2568 typedef struct {
2569 const guint8 * catch_start;
2570 const guint8 * finally_start;
2571 guint catch_size;
2572 guint finally_size;
2574 gboolean use_register;
2575 union {
2576 guint register_number;
2577 char * variable_name;
2580 SwfdecAsObject * scope_object;
2581 } TryData;
2583 static void
2584 swfdec_action_try_data_free (gpointer data)
2586 TryData *try_data = data;
2588 g_return_if_fail (try_data != NULL);
2590 if (!try_data->use_register)
2591 g_free (try_data->variable_name);
2592 g_free (try_data);
2595 static void
2596 swfdec_action_try_end_finally (SwfdecAsContext *cx, SwfdecAsFrame *frame, gpointer data)
2598 SwfdecAsValue *exception_value = data;
2600 g_return_if_fail (SWFDEC_IS_AS_VALUE (exception_value));
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 if (!SWFDEC_IS_MOVIE (cx->frame->target)) {
2781 SWFDEC_FIXME ("target is not a movie in RemoveSprite");
2782 } else if (!SWFDEC_IS_PLAYER (cx)) {
2783 SWFDEC_INFO ("tried using RemoveSprite in a non-SwfdecPlayer context");
2784 } else {
2785 SwfdecMovie *movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
2786 swfdec_as_stack_peek (cx, 1));
2787 if (movie && swfdec_depth_classify (movie->depth) == SWFDEC_DEPTH_CLASS_DYNAMIC) {
2788 SWFDEC_LOG ("removing clip %s", movie->name);
2789 swfdec_movie_remove (movie);
2790 } else {
2791 SWFDEC_INFO ("cannot remove movie");
2794 swfdec_as_stack_pop (cx);
2797 static void
2798 swfdec_action_clone_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2800 SwfdecMovie *movie, *new_movie;
2801 const char *new_name;
2802 int depth;
2804 depth = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1)) - 16384;
2805 new_name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
2806 if (!SWFDEC_IS_MOVIE (cx->frame->target)) {
2807 SWFDEC_FIXME ("target is not a movie in CloneSprite");
2808 } else if (!SWFDEC_IS_PLAYER (cx)) {
2809 SWFDEC_INFO ("tried using CloneSprite in a non-SwfdecPlayer context");
2810 } else {
2811 movie = swfdec_player_get_movie_from_value (SWFDEC_PLAYER (cx),
2812 swfdec_as_stack_peek (cx, 3));
2813 if (movie == NULL) {
2814 SWFDEC_ERROR ("Object is not an SwfdecMovie object");
2815 swfdec_as_stack_pop_n (cx, 3);
2816 return;
2818 new_movie = swfdec_movie_duplicate (movie, new_name, depth);
2819 if (new_movie) {
2820 SWFDEC_LOG ("duplicated %s as %s to depth %u", movie->name, new_movie->name, new_movie->depth);
2823 swfdec_as_stack_pop_n (cx, 3);
2826 /*** PRINT FUNCTIONS ***/
2828 static char *
2829 swfdec_action_print_with (guint action, const guint8 *data, guint len)
2831 if (len != 2) {
2832 SWFDEC_ERROR ("With action requires a length of 2, but got %u", len);
2833 return NULL;
2835 return g_strdup_printf ("With %u", data[0] | (data[1] << 8));
2838 static char *
2839 swfdec_action_print_store_register (guint action, const guint8 *data, guint len)
2841 if (len != 1) {
2842 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
2843 return NULL;
2845 return g_strdup_printf ("StoreRegister %u", (guint) *data);
2848 static char *
2849 swfdec_action_print_set_target (guint action, const guint8 *data, guint len)
2851 if (!memchr (data, 0, len)) {
2852 SWFDEC_ERROR ("SetTarget action does not specify a string");
2853 return NULL;
2855 return g_strconcat ("SetTarget ", data, NULL);
2858 static char *
2859 swfdec_action_print_define_function (guint action, const guint8 *data, guint len)
2861 SwfdecBits bits;
2862 GString *string;
2863 const char *function_name;
2864 guint i, n_args, size;
2865 gboolean v2 = (action == 0x8e);
2867 string = g_string_new (v2 ? "DefineFunction2 " : "DefineFunction ");
2868 swfdec_bits_init_data (&bits, data, len);
2869 /* FIXME: version! */
2870 function_name = swfdec_bits_get_string (&bits, 7);
2871 if (function_name == NULL) {
2872 SWFDEC_ERROR ("could not parse function name");
2873 g_string_free (string, TRUE);
2874 return NULL;
2876 if (*function_name) {
2877 g_string_append (string, function_name);
2878 g_string_append_c (string, ' ');
2880 n_args = swfdec_bits_get_u16 (&bits);
2881 g_string_append_c (string, '(');
2882 if (v2) {
2883 /* n_regs = */ swfdec_bits_get_u8 (&bits);
2884 /* flags = */ swfdec_bits_get_u16 (&bits);
2887 for (i = 0; i < n_args; i++) {
2888 guint preload;
2889 const char *arg_name;
2890 if (v2)
2891 preload = swfdec_bits_get_u8 (&bits);
2892 else
2893 preload = 0;
2894 arg_name = swfdec_bits_get_string (&bits, 7);
2895 if (preload == 0 && (arg_name == NULL || *arg_name == '\0')) {
2896 SWFDEC_ERROR ("empty argument name not allowed");
2897 g_string_free (string, TRUE);
2898 return NULL;
2900 if (i)
2901 g_string_append (string, ", ");
2902 g_string_append (string, arg_name);
2903 if (preload)
2904 g_string_append_printf (string, " (%u)", preload);
2906 g_string_append_c (string, ')');
2907 size = swfdec_bits_get_u16 (&bits);
2908 g_string_append_printf (string, " %u", size);
2909 return g_string_free (string, FALSE);
2912 static char *
2913 swfdec_action_print_get_url2 (guint action, const guint8 *data, guint len)
2915 guint method;
2917 if (len != 1) {
2918 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
2919 return NULL;
2921 method = data[0] >> 6;
2922 if (method == 3) {
2923 SWFDEC_ERROR ("GetURL method 3 invalid");
2924 method = 0;
2926 if (method) {
2927 SWFDEC_FIXME ("implement encoding variables using %s", method == 1 ? "GET" : "POST");
2929 return g_strdup_printf ("GetURL2%s%s%s", method == 0 ? "" : (method == 1 ? " GET" : " POST"),
2930 data[0] & 2 ? " LoadTarget" : "", data[0] & 1 ? " LoadVariables" : "");
2933 static char *
2934 swfdec_action_print_get_url (guint action, const guint8 *data, guint len)
2936 SwfdecBits bits;
2937 char *url, *target, *ret;
2939 swfdec_bits_init_data (&bits, data, len);
2940 url = swfdec_bits_get_string (&bits, 7);
2941 target = swfdec_bits_get_string (&bits, 7);
2942 if (url == NULL) {
2943 SWFDEC_ERROR ("not enough data in GetURL");
2944 url = g_strdup ("???");
2946 if (target == NULL) {
2947 SWFDEC_ERROR ("not enough data in GetURL");
2948 target = g_strdup ("???");
2950 if (swfdec_bits_left (&bits)) {
2951 SWFDEC_WARNING ("leftover bytes in GetURL action");
2953 ret = g_strdup_printf ("GetURL %s %s", url, target);
2954 g_free (url);
2955 g_free (target);
2956 return ret;
2959 static char *
2960 swfdec_action_print_if (guint action, const guint8 *data, guint len)
2962 gint16 offset;
2964 if (len != 2) {
2965 SWFDEC_ERROR ("If action length invalid (is %u, should be 2)", len);
2966 return NULL;
2968 offset = data[0] | (data[1] << 8);
2969 return g_strdup_printf ("If %d", (int) offset);
2972 static char *
2973 swfdec_action_print_jump (guint action, const guint8 *data, guint len)
2975 gint16 offset;
2977 if (len != 2) {
2978 SWFDEC_ERROR ("If action length invalid (is %u, should be 2)", len);
2979 return NULL;
2981 offset = data[0] | (data[1] << 8);
2982 return g_strdup_printf ("Jump %d", (int) offset);
2985 static char *
2986 swfdec_action_print_push (guint action, const guint8 *data, guint len)
2988 gboolean first = TRUE;
2989 SwfdecBits bits;
2990 GString *string = g_string_new ("Push");
2992 swfdec_bits_init_data (&bits, data, len);
2993 while (swfdec_bits_left (&bits)) {
2994 guint type = swfdec_bits_get_u8 (&bits);
2995 if (first)
2996 g_string_append (string, " ");
2997 else
2998 g_string_append (string, ", ");
2999 first = FALSE;
3000 switch (type) {
3001 case 0: /* string */
3003 /* FIXME: need version! */
3004 char *s = swfdec_bits_get_string (&bits, 7);
3005 if (!s) {
3006 g_string_free (string, TRUE);
3007 return NULL;
3009 g_string_append_c (string, '"');
3010 g_string_append (string, s);
3011 g_string_append_c (string, '"');
3012 g_free (s);
3013 break;
3015 case 1: /* float */
3016 g_string_append_printf (string, "%g", swfdec_bits_get_float (&bits));
3017 break;
3018 case 2: /* null */
3019 g_string_append (string, "null");
3020 break;
3021 case 3: /* undefined */
3022 g_string_append (string, "void");
3023 break;
3024 case 4: /* register */
3025 g_string_append_printf (string, "Register %u", swfdec_bits_get_u8 (&bits));
3026 break;
3027 case 5: /* boolean */
3028 g_string_append (string, swfdec_bits_get_u8 (&bits) ? "True" : "False");
3029 break;
3030 case 6: /* double */
3031 g_string_append_printf (string, "%g", swfdec_bits_get_double (&bits));
3032 break;
3033 case 7: /* 32bit int */
3034 g_string_append_printf (string, "%d", swfdec_bits_get_s32 (&bits));
3035 break;
3036 case 8: /* 8bit ConstantPool address */
3037 g_string_append_printf (string, "Pool %u", swfdec_bits_get_u8 (&bits));
3038 break;
3039 case 9: /* 16bit ConstantPool address */
3040 g_string_append_printf (string, "Pool %u", swfdec_bits_get_u16 (&bits));
3041 break;
3042 default:
3043 SWFDEC_ERROR ("Push: unknown type %u, skipping", type);
3044 break;
3047 return g_string_free (string, FALSE);
3050 /* NB: constant pool actions are special in that they are called at init time */
3051 static char *
3052 swfdec_action_print_constant_pool (guint action, const guint8 *data, guint len)
3054 guint i;
3055 GString *string;
3056 SwfdecConstantPool *pool;
3057 SwfdecBuffer *buffer;
3059 /* FIXME: version */
3060 buffer = swfdec_buffer_new_static (data, len);
3061 pool = swfdec_constant_pool_new (NULL, buffer, 6);
3062 if (pool == NULL)
3063 return g_strdup ("ConstantPool (invalid)");
3064 string = g_string_new ("ConstantPool");
3065 for (i = 0; i < swfdec_constant_pool_size (pool); i++) {
3066 g_string_append (string, i ? ", " : " ");
3067 g_string_append (string, swfdec_constant_pool_get (pool, i));
3068 g_string_append_printf (string, " (%u)", i);
3070 swfdec_constant_pool_unref (pool);
3071 swfdec_buffer_unref (buffer);
3072 return g_string_free (string, FALSE);
3075 static char *
3076 swfdec_action_print_wait_for_frame2 (guint action, const guint8 *data, guint len)
3078 if (len < 1) {
3079 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
3080 return NULL;
3082 return g_strdup_printf ("WaitForFrame2 %u", (guint) *data);
3085 static char *
3086 swfdec_action_print_goto_frame2 (guint action, const guint8 *data, guint len)
3088 gboolean play, bias;
3089 SwfdecBits bits;
3091 swfdec_bits_init_data (&bits, data, len);
3092 if (swfdec_bits_getbits (&bits, 6)) {
3093 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
3095 bias = swfdec_bits_getbit (&bits);
3096 play = swfdec_bits_getbit (&bits);
3097 if (bias) {
3098 return g_strdup_printf ("GotoFrame2 %s +%u", play ? "play" : "stop",
3099 swfdec_bits_get_u16 (&bits));
3100 } else {
3101 return g_strdup_printf ("GotoFrame2 %s", play ? "play" : "stop");
3105 static char *
3106 swfdec_action_print_goto_frame (guint action, const guint8 *data, guint len)
3108 guint frame;
3110 if (len != 2)
3111 return NULL;
3113 frame = data[0] | (data[1] << 8);
3114 return g_strdup_printf ("GotoFrame %u", frame);
3117 static char *
3118 swfdec_action_print_goto_label (guint action, const guint8 *data, guint len)
3120 if (!memchr (data, 0, len)) {
3121 SWFDEC_ERROR ("GotoLabel action does not specify a string");
3122 return NULL;
3125 return g_strdup_printf ("GotoLabel %s", data);
3128 static char *
3129 swfdec_action_print_wait_for_frame (guint action, const guint8 *data, guint len)
3131 guint frame, jump;
3133 if (len != 3)
3134 return NULL;
3136 frame = data[0] | (data[1] << 8);
3137 jump = data[2];
3138 return g_strdup_printf ("WaitForFrame %u %u", frame, jump);
3141 /*** BIG FUNCTION TABLE ***/
3143 const SwfdecActionSpec swfdec_as_actions[256] = {
3144 /* version 1 */
3145 [SWFDEC_AS_ACTION_END] = { "End", NULL, 0, 0, swfdec_action_end, 1 },
3146 [SWFDEC_AS_ACTION_NEXT_FRAME] = { "NextFrame", NULL, 0, 0, swfdec_action_next_frame, 1 },
3147 [SWFDEC_AS_ACTION_PREVIOUS_FRAME] = { "PreviousFrame", NULL, 0, 0, swfdec_action_previous_frame, 1 },
3148 [SWFDEC_AS_ACTION_PLAY] = { "Play", NULL, 0, 0, swfdec_action_play, 1 },
3149 [SWFDEC_AS_ACTION_STOP] = { "Stop", NULL, 0, 0, swfdec_action_stop, 1 },
3150 [SWFDEC_AS_ACTION_TOGGLE_QUALITY] = { "ToggleQuality", NULL, -1, -1, NULL, 1 },
3151 /* version 2 */
3152 [SWFDEC_AS_ACTION_STOP_SOUNDS] = { "StopSounds", NULL, 0, 0, swfdec_action_stop_sounds, 2 },
3153 /* version 4 */
3154 [SWFDEC_AS_ACTION_ADD] = { "Add", NULL, 2, 1, swfdec_action_binary, 4 },
3155 [SWFDEC_AS_ACTION_SUBTRACT] = { "Subtract", NULL, 2, 1, swfdec_action_binary, 4 },
3156 [SWFDEC_AS_ACTION_MULTIPLY] = { "Multiply", NULL, 2, 1, swfdec_action_binary, 4 },
3157 [SWFDEC_AS_ACTION_DIVIDE] = { "Divide", NULL, 2, 1, swfdec_action_binary, 4 },
3158 [SWFDEC_AS_ACTION_EQUALS] = { "Equals", NULL, 2, 1, swfdec_action_old_compare, 4 },
3159 [SWFDEC_AS_ACTION_LESS] = { "Less", NULL, 2, 1, swfdec_action_old_compare, 4 },
3160 [SWFDEC_AS_ACTION_AND] = { "And", NULL, 2, 1, swfdec_action_logical, 4 },
3161 [SWFDEC_AS_ACTION_OR] = { "Or", NULL, 2, 1, swfdec_action_logical, 4 },
3162 [SWFDEC_AS_ACTION_NOT] = { "Not", NULL, 1, 1, swfdec_action_not, 4 },
3163 [SWFDEC_AS_ACTION_STRING_EQUALS] = { "StringEquals", NULL, 2, 1, swfdec_action_string_compare, 4 },
3164 [SWFDEC_AS_ACTION_STRING_LENGTH] = { "StringLength", NULL, 1, 1, swfdec_action_string_length, 4 },
3165 [SWFDEC_AS_ACTION_STRING_EXTRACT] = { "StringExtract", NULL, 3, 1, swfdec_action_string_extract, 4 },
3166 [SWFDEC_AS_ACTION_POP] = { "Pop", NULL, 1, 0, swfdec_action_pop, 4 },
3167 [SWFDEC_AS_ACTION_TO_INTEGER] = { "ToInteger", NULL, 1, 1, swfdec_action_to_integer, 4 },
3168 [SWFDEC_AS_ACTION_GET_VARIABLE] = { "GetVariable", NULL, 1, 1, swfdec_action_get_variable, 4 },
3169 [SWFDEC_AS_ACTION_SET_VARIABLE] = { "SetVariable", NULL, 2, 0, swfdec_action_set_variable, 4 },
3170 /* version 3 */
3171 [SWFDEC_AS_ACTION_SET_TARGET2] = { "SetTarget2", NULL, 1, 0, swfdec_action_set_target2, 3 },
3172 /* version 4 */
3173 [SWFDEC_AS_ACTION_STRING_ADD] = { "StringAdd", NULL, 2, 1, swfdec_action_string_add, 4 },
3174 [SWFDEC_AS_ACTION_GET_PROPERTY] = { "GetProperty", NULL, 2, 1, swfdec_action_get_property, 4 },
3175 [SWFDEC_AS_ACTION_SET_PROPERTY] = { "SetProperty", NULL, 3, 0, swfdec_action_set_property, 4 },
3176 [SWFDEC_AS_ACTION_CLONE_SPRITE] = { "CloneSprite", NULL, 3, 0, swfdec_action_clone_sprite, 4 },
3177 [SWFDEC_AS_ACTION_REMOVE_SPRITE] = { "RemoveSprite", NULL, 1, 0, swfdec_action_remove_sprite, 4 },
3178 [SWFDEC_AS_ACTION_TRACE] = { "Trace", NULL, 1, 0, swfdec_action_trace, 4 },
3179 [SWFDEC_AS_ACTION_START_DRAG] = { "StartDrag", NULL, -1, 0, swfdec_action_start_drag, 4 },
3180 [SWFDEC_AS_ACTION_END_DRAG] = { "EndDrag", NULL, 0, 0, swfdec_action_end_drag, 4 },
3181 [SWFDEC_AS_ACTION_STRING_LESS] = { "StringLess", NULL, 2, 1, swfdec_action_string_compare, 4 },
3182 /* version 7 */
3183 [SWFDEC_AS_ACTION_THROW] = { "Throw", NULL, 1, 0, swfdec_action_throw, 7 },
3184 [SWFDEC_AS_ACTION_CAST] = { "Cast", NULL, 2, 1, swfdec_action_cast, 7 },
3185 [SWFDEC_AS_ACTION_IMPLEMENTS] = { "Implements", NULL, -1, 0, swfdec_action_implements, 7 },
3186 /* version 4 */
3187 [SWFDEC_AS_ACTION_RANDOM] = { "RandomNumber", NULL, 1, 1, swfdec_action_random_number, 4 },
3188 [SWFDEC_AS_ACTION_MB_STRING_LENGTH] = { "MBStringLength", NULL, 1, 1, swfdec_action_string_length, 4 },
3189 [SWFDEC_AS_ACTION_CHAR_TO_ASCII] = { "CharToAscii", NULL, 1, 1, swfdec_action_char_to_ascii, 4 },
3190 [SWFDEC_AS_ACTION_ASCII_TO_CHAR] = { "AsciiToChar", NULL, 1, 1, swfdec_action_ascii_to_char, 4 },
3191 [SWFDEC_AS_ACTION_GET_TIME] = { "GetTime", NULL, 0, 1, swfdec_action_get_time, 4 },
3192 [SWFDEC_AS_ACTION_MB_STRING_EXTRACT] = { "MBStringExtract", NULL, 3, 1, swfdec_action_string_extract, 4 },
3193 [SWFDEC_AS_ACTION_MB_CHAR_TO_ASCII] = { "MBCharToAscii", NULL, 1, 1, swfdec_action_char_to_ascii, 4 },
3194 [SWFDEC_AS_ACTION_MB_ASCII_TO_CHAR] = { "MBAsciiToChar", NULL, 1, 1, swfdec_action_ascii_to_char, 4 },
3195 /* version 5 */
3196 [SWFDEC_AS_ACTION_DELETE] = { "Delete", NULL, 2, 1, swfdec_action_delete, 5 },
3197 [SWFDEC_AS_ACTION_DELETE2] = { "Delete2", NULL, 1, 1, swfdec_action_delete2, 5 },
3198 [SWFDEC_AS_ACTION_DEFINE_LOCAL] = { "DefineLocal", NULL, 2, 0, swfdec_action_define_local, 5 },
3199 [SWFDEC_AS_ACTION_CALL_FUNCTION] = { "CallFunction", NULL, -1, 1, swfdec_action_call_function, 5 },
3200 [SWFDEC_AS_ACTION_RETURN] = { "Return", NULL, 1, 0, swfdec_action_return, 5 },
3201 [SWFDEC_AS_ACTION_MODULO] = { "Modulo", NULL, 2, 1, swfdec_action_modulo, 5 },
3202 [SWFDEC_AS_ACTION_NEW_OBJECT] = { "NewObject", NULL, -1, 1, swfdec_action_new_object, 5 },
3203 [SWFDEC_AS_ACTION_DEFINE_LOCAL2] = { "DefineLocal2", NULL, 1, 0, swfdec_action_define_local2, 5 },
3204 [SWFDEC_AS_ACTION_INIT_ARRAY] = { "InitArray", NULL, -1, 1, swfdec_action_init_array, 5 },
3205 [SWFDEC_AS_ACTION_INIT_OBJECT] = { "InitObject", NULL, -1, 1, swfdec_action_init_object, 5 },
3206 [SWFDEC_AS_ACTION_TYPE_OF] = { "TypeOf", NULL, 1, 1, swfdec_action_type_of, 5 },
3207 [SWFDEC_AS_ACTION_TARGET_PATH] = { "TargetPath", NULL, 1, 1, swfdec_action_target_path, 5 },
3208 [SWFDEC_AS_ACTION_ENUMERATE] = { "Enumerate", NULL, 1, -1, swfdec_action_enumerate, 5 },
3209 [SWFDEC_AS_ACTION_ADD2] = { "Add2", NULL, 2, 1, swfdec_action_add2, 5 },
3210 [SWFDEC_AS_ACTION_LESS2] = { "Less2", NULL, 2, 1, swfdec_action_new_comparison, 5 },
3211 [SWFDEC_AS_ACTION_EQUALS2] = { "Equals2", NULL, 2, 1, swfdec_action_equals2, 5 },
3212 [SWFDEC_AS_ACTION_TO_NUMBER] = { "ToNumber", NULL, 1, 1, swfdec_action_to_number, 5 },
3213 [SWFDEC_AS_ACTION_TO_STRING] = { "ToString", NULL, 1, 1, swfdec_action_to_string, 5 },
3214 [SWFDEC_AS_ACTION_PUSH_DUPLICATE] = { "PushDuplicate", NULL, 1, 2, swfdec_action_push_duplicate, 5 },
3215 [SWFDEC_AS_ACTION_SWAP] = { "Swap", NULL, 2, 2, swfdec_action_swap, 5 },
3216 /* version 4 */
3217 [SWFDEC_AS_ACTION_GET_MEMBER] = { "GetMember", NULL, 2, 1, swfdec_action_get_member, 4 },
3218 [SWFDEC_AS_ACTION_SET_MEMBER] = { "SetMember", NULL, 3, 0, swfdec_action_set_member, 4 },
3219 /* version 5 */
3220 [SWFDEC_AS_ACTION_INCREMENT] = { "Increment", NULL, 1, 1, swfdec_action_increment, 5 },
3221 [SWFDEC_AS_ACTION_DECREMENT] = { "Decrement", NULL, 1, 1, swfdec_action_decrement, 5 },
3222 [SWFDEC_AS_ACTION_CALL_METHOD] = { "CallMethod", NULL, -1, 1, swfdec_action_call_method, 5 },
3223 [SWFDEC_AS_ACTION_NEW_METHOD] = { "NewMethod", NULL, -1, 1, swfdec_action_new_method, 5 },
3224 /* version 6 */
3225 [SWFDEC_AS_ACTION_INSTANCE_OF] = { "InstanceOf", NULL, 2, 1, swfdec_action_instance_of, 6 },
3226 [SWFDEC_AS_ACTION_ENUMERATE2] = { "Enumerate2", NULL, 1, -1, swfdec_action_enumerate2, 6 },
3227 [SWFDEC_AS_ACTION_BREAKPOINT] = { "Breakpoint", NULL, -1, -1, NULL, 6 },
3228 /* version 5 */
3229 [SWFDEC_AS_ACTION_BIT_AND] = { "BitAnd", NULL, 2, 1, swfdec_action_bitwise, 5 },
3230 [SWFDEC_AS_ACTION_BIT_OR] = { "BitOr", NULL, 2, 1, swfdec_action_bitwise, 5 },
3231 [SWFDEC_AS_ACTION_BIT_XOR] = { "BitXor", NULL, 2, 1, swfdec_action_bitwise, 5 },
3232 [SWFDEC_AS_ACTION_BIT_LSHIFT] = { "BitLShift", NULL, 2, 1, swfdec_action_shift, 5 },
3233 [SWFDEC_AS_ACTION_BIT_RSHIFT] = { "BitRShift", NULL, 2, 1, swfdec_action_shift, 5 },
3234 [SWFDEC_AS_ACTION_BIT_URSHIFT] = { "BitURShift", NULL, 2, 1, swfdec_action_shift, 5 },
3235 /* version 6 */
3236 [SWFDEC_AS_ACTION_STRICT_EQUALS] = { "StrictEquals", NULL, 2, 1, swfdec_action_strict_equals, 6 },
3237 [SWFDEC_AS_ACTION_GREATER] = { "Greater", NULL, 2, 1, swfdec_action_new_comparison, 6 },
3238 [SWFDEC_AS_ACTION_STRING_GREATER] = { "StringGreater", NULL, 2, 1, swfdec_action_string_compare, 6 },
3239 /* version 7 */
3240 [SWFDEC_AS_ACTION_EXTENDS] = { "Extends", NULL, 2, 0, swfdec_action_extends, 7 },
3241 /* version 1 */
3242 [SWFDEC_AS_ACTION_GOTO_FRAME] = { "GotoFrame", swfdec_action_print_goto_frame, 0, 0, swfdec_action_goto_frame, 1 },
3243 [SWFDEC_AS_ACTION_GET_URL] = { "GetURL", swfdec_action_print_get_url, 0, 0, swfdec_action_get_url, 1 },
3244 /* version 5 */
3245 [SWFDEC_AS_ACTION_STORE_REGISTER] = { "StoreRegister", swfdec_action_print_store_register, 1, 1, swfdec_action_store_register, 5 },
3246 [SWFDEC_AS_ACTION_CONSTANT_POOL] = { "ConstantPool", swfdec_action_print_constant_pool, 0, 0, swfdec_action_constant_pool, 5 },
3247 [SWFDEC_AS_ACTION_STRICT_MODE] = { "StrictMode", NULL, -1, -1, NULL, 5 },
3248 /* version 1 */
3249 [SWFDEC_AS_ACTION_WAIT_FOR_FRAME] = { "WaitForFrame", swfdec_action_print_wait_for_frame, 0, 0, swfdec_action_wait_for_frame, 1 },
3250 [SWFDEC_AS_ACTION_SET_TARGET] = { "SetTarget", swfdec_action_print_set_target, 0, 0, swfdec_action_set_target, 1 },
3251 /* version 3 */
3252 [SWFDEC_AS_ACTION_GOTO_LABEL] = { "GotoLabel", swfdec_action_print_goto_label, 0, 0, swfdec_action_goto_label, 3 },
3253 /* version 4 */
3254 [SWFDEC_AS_ACTION_WAIT_FOR_FRAME2] = { "WaitForFrame2", swfdec_action_print_wait_for_frame2, 1, 0, swfdec_action_wait_for_frame2, 4 },
3255 /* version 7 */
3256 [SWFDEC_AS_ACTION_DEFINE_FUNCTION2] = { "DefineFunction2", swfdec_action_print_define_function, 0, -1, swfdec_action_define_function, 7 },
3257 [SWFDEC_AS_ACTION_TRY] = { "Try", NULL, 0, 0, swfdec_action_try, 7 },
3258 /* version 5 */
3259 [SWFDEC_AS_ACTION_WITH] = { "With", swfdec_action_print_with, 1, 0, swfdec_action_with, 5 },
3260 /* version 4 */
3261 [SWFDEC_AS_ACTION_PUSH] = { "Push", swfdec_action_print_push, 0, -1, swfdec_action_push, 4 },
3262 [SWFDEC_AS_ACTION_JUMP] = { "Jump", swfdec_action_print_jump, 0, 0, swfdec_action_jump, 4 },
3263 [SWFDEC_AS_ACTION_GET_URL2] = { "GetURL2", swfdec_action_print_get_url2, 2, 0, swfdec_action_get_url2, 4 },
3264 /* version 5 */
3265 [SWFDEC_AS_ACTION_DEFINE_FUNCTION] = { "DefineFunction", swfdec_action_print_define_function, 0, -1, swfdec_action_define_function, 5 },
3266 /* version 4 */
3267 [SWFDEC_AS_ACTION_IF] = { "If", swfdec_action_print_if, 1, 0, swfdec_action_if, 4 },
3268 [SWFDEC_AS_ACTION_CALL] = { "Call", NULL, -1, -1, NULL, 4 },
3269 [SWFDEC_AS_ACTION_GOTO_FRAME2] = { "GotoFrame2", swfdec_action_print_goto_frame2, 1, 0, swfdec_action_goto_frame2, 4 }