rewrite GetVariable stuff once again - this time it works
[swfdec.git] / libswfdec / swfdec_as_interpret.c
blob5cd975d20390af44656302dd0b34fd404e3f4d1b
1 /* Swfdec
2 * Copyright (C) 2007 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301 USA
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
23 #include "swfdec_as_interpret.h"
24 #include "swfdec_as_array.h"
25 #include "swfdec_as_context.h"
26 #include "swfdec_as_frame_internal.h"
27 #include "swfdec_as_function.h"
28 #include "swfdec_as_script_function.h"
29 #include "swfdec_as_stack.h"
30 #include "swfdec_as_string.h"
31 #include "swfdec_as_strings.h"
32 #include "swfdec_as_super.h"
33 #include "swfdec_debug.h"
35 #include <errno.h>
36 #include <math.h>
37 #include <string.h>
38 #include "swfdec_decoder.h"
39 #include "swfdec_movie.h"
40 #include "swfdec_player_internal.h"
41 #include "swfdec_sprite.h"
42 #include "swfdec_sprite_movie.h"
43 #include "swfdec_swf_instance.h"
45 /* Define this to get SWFDEC_WARN'd about missing properties of objects.
46 * This can be useful to find out about unimplemented native properties,
47 * but usually just causes a lot of spam. */
48 //#define SWFDEC_WARN_MISSING_PROPERTIES
50 /*** SUPPORT FUNCTIONS ***/
52 #define swfdec_action_has_register(cx, i) \
53 ((i) < (cx)->frame->n_registers)
55 /*** ALL THE ACTION IS HERE ***/
57 static void
58 swfdec_action_stop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
60 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
61 SWFDEC_SPRITE_MOVIE (cx->frame->target)->playing = FALSE;
62 else
63 SWFDEC_ERROR ("no movie to stop");
66 static void
67 swfdec_action_play (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
69 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
70 SWFDEC_SPRITE_MOVIE (cx->frame->target)->playing = TRUE;
71 else
72 SWFDEC_ERROR ("no movie to play");
75 static void
76 swfdec_action_next_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
78 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
79 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
80 if (movie->frame < movie->n_frames) {
81 swfdec_sprite_movie_goto (movie, movie->frame + 1);
82 } else {
83 SWFDEC_INFO ("can't execute nextFrame, already at last frame");
85 } else {
86 SWFDEC_ERROR ("no movie to nextFrame on");
90 static void
91 swfdec_action_previous_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
93 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
94 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
95 if (movie->frame > 1) {
96 swfdec_sprite_movie_goto (movie, movie->frame - 1);
97 } else {
98 SWFDEC_INFO ("can't execute previousFrame, already at first frame");
100 } else {
101 SWFDEC_ERROR ("no movie to previousFrame on");
105 static void
106 swfdec_action_goto_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
108 guint frame;
110 if (len != 2) {
111 SWFDEC_ERROR ("GotoFrame action length invalid (is %u, should be 2", len);
112 return;
114 frame = GUINT16_FROM_LE (*((guint16 *) data));
115 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
116 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
117 swfdec_sprite_movie_goto (movie, frame + 1);
118 movie->playing = FALSE;
119 } else {
120 SWFDEC_ERROR ("no movie to goto on");
124 static void
125 swfdec_action_goto_label (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
127 if (!memchr (data, 0, len)) {
128 SWFDEC_ERROR ("GotoLabel action does not specify a string");
129 return;
132 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
133 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
134 int frame;
135 if (movie->sprite == NULL ||
136 (frame = swfdec_sprite_get_frame (movie->sprite, (const char *) data)) == -1)
137 return;
138 swfdec_sprite_movie_goto (movie, frame + 1);
139 movie->playing = FALSE;
140 } else {
141 SWFDEC_ERROR ("no movie to goto on");
145 /* returns: frame to go to or 0 on error */
146 static guint
147 swfdec_value_to_frame (SwfdecAsContext *cx, SwfdecSpriteMovie *movie, SwfdecAsValue *val)
149 int frame;
151 if (movie->sprite == NULL)
152 return 0;
153 if (SWFDEC_AS_VALUE_IS_STRING (val)) {
154 const char *name = SWFDEC_AS_VALUE_GET_STRING (val);
155 double d;
156 if (strchr (name, ':')) {
157 SWFDEC_ERROR ("FIXME: handle targets");
159 /* treat valid encoded numbers as numbers, otherwise assume it's a frame label */
160 d = swfdec_as_value_to_number (cx, val);
161 if (isnan (d))
162 frame = swfdec_sprite_get_frame (movie->sprite, name) + 1;
163 else
164 frame = d;
165 } else if (SWFDEC_AS_VALUE_IS_NUMBER (val)) {
166 frame = swfdec_as_value_to_integer (cx, val);
167 } else {
168 SWFDEC_WARNING ("cannot convert value to frame number");
169 /* FIXME: how do we treat undefined etc? */
170 frame = 0;
172 return frame <= 0 ? 0 : frame;
175 static void
176 swfdec_action_goto_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
178 SwfdecBits bits;
179 guint bias;
180 gboolean play;
181 SwfdecAsValue *val;
183 swfdec_bits_init_data (&bits, data, len);
184 if (swfdec_bits_getbits (&bits, 6)) {
185 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
187 bias = swfdec_bits_getbit (&bits);
188 play = swfdec_bits_getbit (&bits);
189 if (bias) {
190 bias = swfdec_bits_get_u16 (&bits);
192 val = swfdec_as_stack_peek (cx, 1);
193 /* now set it */
194 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
195 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
196 guint frame = swfdec_value_to_frame (cx, movie, val);
197 if (frame > 0) {
198 frame += bias;
199 frame = CLAMP (frame, 1, movie->n_frames);
200 swfdec_sprite_movie_goto (movie, frame);
201 movie->playing = play;
203 } else {
204 SWFDEC_ERROR ("no movie to GotoFrame2 on");
206 swfdec_as_stack_pop (cx);
209 static void
210 swfdec_script_skip_actions (SwfdecAsContext *cx, guint jump)
212 SwfdecScript *script = cx->frame->script;
213 guint8 *pc = cx->frame->pc;
214 guint8 *endpc = script->buffer->data + script->buffer->length;
216 /* jump instructions */
217 do {
218 if (pc >= endpc)
219 break;
220 if (*pc & 0x80) {
221 if (pc + 2 >= endpc)
222 break;
223 pc += 3 + GUINT16_FROM_LE (*((guint16 *) (pc + 1)));
224 } else {
225 pc++;
227 } while (jump-- > 0);
228 cx->frame->pc = pc;
231 #if 0
232 static void
233 swfdec_action_wait_for_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
235 jsval val;
237 if (len != 1) {
238 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
239 return JS_FALSE;
241 val = cx->fp->sp[-1];
242 cx->fp->sp--;
243 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
244 SwfdecMovie *movie = SWFDEC_MOVIE (cx->frame->target);
245 int frame = swfdec_value_to_frame (cx, movie, val);
246 guint jump = data[2];
247 guint loaded;
248 if (frame < 0)
249 return JS_TRUE;
250 if (SWFDEC_IS_ROOT_MOVIE (movie)) {
251 SwfdecDecoder *dec = SWFDEC_ROOT_MOVIE (movie)->decoder;
252 loaded = dec->frames_loaded;
253 g_assert (loaded <= movie->n_frames);
254 } else {
255 loaded = movie->n_frames;
257 if (loaded <= (guint) frame)
258 swfdec_script_skip_actions (cx, jump);
259 } else {
260 SWFDEC_ERROR ("no movie to WaitForFrame2 on");
262 return JS_TRUE;
264 #endif
266 static void
267 swfdec_action_wait_for_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
269 SwfdecSpriteMovie *movie;
270 guint frame, jump, loaded;
272 if (len != 3) {
273 SWFDEC_ERROR ("WaitForFrame action length invalid (is %u, should be 3", len);
274 return;
276 if (!SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
277 SWFDEC_ERROR ("no movie for WaitForFrame");
278 return;
281 movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
282 frame = data[0] || (data[1] << 8);
283 jump = data[2];
284 if (SWFDEC_MOVIE (movie)->swf->movie == movie) {
285 SwfdecDecoder *dec = SWFDEC_MOVIE (movie)->swf->decoder;
286 loaded = dec->frames_loaded;
287 g_assert (loaded <= movie->n_frames);
288 if (loaded == dec->frames_total)
289 loaded = G_MAXUINT;
290 } else {
291 loaded = G_MAXUINT;
293 if (loaded <= frame)
294 swfdec_script_skip_actions (cx, jump);
297 static void
298 swfdec_action_constant_pool (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
300 SwfdecConstantPool *pool;
301 SwfdecAsFrame *frame;
303 frame = cx->frame;
304 pool = swfdec_constant_pool_new_from_action (data, len, cx->version);
305 if (pool == NULL)
306 return;
307 swfdec_constant_pool_attach_to_context (pool, cx);
308 if (frame->constant_pool)
309 swfdec_constant_pool_free (frame->constant_pool);
310 frame->constant_pool = pool;
311 if (frame->constant_pool_buffer)
312 swfdec_buffer_unref (frame->constant_pool_buffer);
313 frame->constant_pool_buffer = swfdec_buffer_new_subbuffer (frame->script->buffer,
314 data - frame->script->buffer->data, len);
317 static void
318 swfdec_action_push (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
320 SwfdecBits bits;
322 swfdec_bits_init_data (&bits, data, len);
323 while (swfdec_bits_left (&bits)) {
324 guint type = swfdec_bits_get_u8 (&bits);
325 SWFDEC_LOG ("push type %u", type);
326 swfdec_as_stack_ensure_free (cx, 1);
327 switch (type) {
328 case 0: /* string */
330 char *s = swfdec_bits_get_string_with_version (&bits, cx->version);
331 if (s == NULL)
332 return;
333 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
334 swfdec_as_context_give_string (cx, s));
335 break;
337 case 1: /* float */
338 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_push (cx),
339 swfdec_bits_get_float (&bits));
340 break;
341 case 2: /* null */
342 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
343 break;
344 case 3: /* undefined */
345 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
346 break;
347 case 4: /* register */
349 guint regnum = swfdec_bits_get_u8 (&bits);
350 if (!swfdec_action_has_register (cx, regnum)) {
351 SWFDEC_ERROR ("cannot Push register %u: not enough registers", regnum);
352 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
353 } else {
354 *swfdec_as_stack_push (cx) = cx->frame->registers[regnum];
356 break;
358 case 5: /* boolean */
359 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx),
360 swfdec_bits_get_u8 (&bits) ? TRUE : FALSE);
361 break;
362 case 6: /* double */
363 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_push (cx),
364 swfdec_bits_get_double (&bits));
365 break;
366 case 7: /* 32bit int */
367 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_push (cx),
368 (int) swfdec_bits_get_u32 (&bits));
369 break;
370 case 8: /* 8bit ConstantPool address */
372 guint i = swfdec_bits_get_u8 (&bits);
373 SwfdecConstantPool *pool = cx->frame->constant_pool;
374 if (pool == NULL) {
375 SWFDEC_ERROR ("no constant pool to push from");
376 return;
378 if (i >= swfdec_constant_pool_size (pool)) {
379 SWFDEC_ERROR ("constant pool index %u too high - only %u elements",
380 i, swfdec_constant_pool_size (pool));
381 return;
383 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
384 swfdec_constant_pool_get (pool, i));
385 break;
387 case 9: /* 16bit ConstantPool address */
389 guint i = swfdec_bits_get_u16 (&bits);
390 SwfdecConstantPool *pool = cx->frame->constant_pool;
391 if (pool == NULL) {
392 SWFDEC_ERROR ("no constant pool to push from");
393 return;
395 if (i >= swfdec_constant_pool_size (pool)) {
396 SWFDEC_ERROR ("constant pool index %u too high - only %u elements",
397 i, swfdec_constant_pool_size (pool));
398 return;
400 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
401 swfdec_constant_pool_get (pool, i));
402 break;
404 default:
405 SWFDEC_ERROR ("Push: type %u not implemented", type);
406 return;
411 static SwfdecAsObject *
412 super_special_movie_lookup_magic (SwfdecAsObject *o, const char *name)
414 SwfdecAsValue val;
416 if (SWFDEC_IS_MOVIE (o)) {
417 SwfdecMovie *ret = swfdec_movie_get_by_name (SWFDEC_MOVIE (o), name);
418 if (ret)
419 return SWFDEC_AS_OBJECT (ret);
421 if (!swfdec_as_object_get_variable (o, name, &val))
422 return NULL;
423 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val))
424 return NULL;
425 return SWFDEC_AS_VALUE_GET_OBJECT (&val);
428 static SwfdecAsObject *
429 swfdec_action_get_movie_by_slash_path (SwfdecAsContext *cx, const char *path)
431 SwfdecAsObject *o;
433 o = cx->frame->target;
434 if (!SWFDEC_IS_MOVIE (o))
435 return NULL;
436 if (*path == '/') {
437 o = SWFDEC_AS_OBJECT (swfdec_movie_get_root (SWFDEC_MOVIE (o)));
438 path++;
440 while (*path) {
441 char *slash = strchr (path, '/');
442 const char *name;
443 if (slash) {
444 if (slash == path)
445 return NULL;
446 name = swfdec_as_context_give_string (cx, g_strndup (path, slash - path));
447 path = slash + 1;
448 } else {
449 name = swfdec_as_context_get_string (cx, path);
450 path += strlen (path);
452 o = super_special_movie_lookup_magic (o, name);
453 if (!SWFDEC_IS_MOVIE (o))
454 return NULL;
456 return o;
459 static SwfdecAsObject *
460 swfdec_action_lookup_object (SwfdecAsContext *cx, const char *path, const char *end)
462 SwfdecAsObject *o = cx->frame->target;
463 gboolean dot_allowed = TRUE;
464 const char *start;
466 g_assert (*end == '.' || *end == ':');
467 if (path == end)
468 return NULL;
470 if (path[0] == '/') {
471 if (!SWFDEC_IS_MOVIE (o))
472 return NULL;
473 o = SWFDEC_AS_OBJECT (swfdec_movie_get_root (SWFDEC_MOVIE (o)));
474 path++;
475 dot_allowed = FALSE;
477 while (path < end) {
478 for (start = path; path < end; path++) {
479 if (dot_allowed && path[0] == '.') {
480 if (end - path >= 2 && path[1] == '.') {
481 dot_allowed = FALSE;
482 continue;
484 } else if (path[0] == ':') {
485 if (path[1] == '/')
486 continue;
487 if (path == start) {
488 start++;
489 continue;
491 } else if (path[0] == '/') {
492 dot_allowed = FALSE;
493 } else if (path - start < 127) {
494 continue;
497 break;
500 /* parse variable */
501 if (start[0] == '.' && start[1] == '.' && start + 2 == path) {
502 /* ".." goes back to parent */
503 if (!SWFDEC_IS_MOVIE (o))
504 return NULL;
505 o = SWFDEC_AS_OBJECT (SWFDEC_MOVIE (o)->parent);
506 if (o == NULL)
507 return NULL;
508 } else {
509 o = super_special_movie_lookup_magic (o, swfdec_as_context_give_string (cx,
510 g_strndup (start, path - start)));
511 if (o == NULL)
512 return NULL;
514 if (path - start < 127)
515 path++;
518 return o;
522 * swfdec_action_get_movie_by_path:
523 * @cx: a #SwfdecAsContext
524 * @path: the path to look up
525 * @object: pointer that takes the object that was looked up. The object may be
526 * %NULL.
527 * @variable: pointer that takes variable part of the path. The variable will
528 * be either %NULL or a non-gc'ed variable name.
530 * Looks up a Flash4-compatible path using "/", ":" and "." style syntax.
532 * Returns: The #SwfdecMovie that was looked up or %NULL if the path does not
533 * specify a valid movie.
535 static gboolean
536 swfdec_action_get_movie_by_path (SwfdecAsContext *cx, const char *path,
537 SwfdecAsObject **object, const char **variable)
539 SwfdecAsObject *movie;
540 char *end, *s;
542 g_assert (path != NULL);
543 g_assert (object != NULL);
544 g_assert (variable != NULL);
545 g_assert (cx->frame != NULL);
547 /* find dot or colon */
548 end = strpbrk (path, ".:");
550 /* if no dot or colon, look up slash-path */
551 if (end == NULL) {
552 /* shortcut for the general case */
553 if (strchr (path, '/') == NULL) {
554 *object = NULL;
555 *variable = path;
556 return TRUE;
559 *variable = NULL;
560 movie = swfdec_action_get_movie_by_slash_path (cx, path);
561 if (movie == NULL) {
562 *object = NULL;
563 return FALSE;
564 } else {
565 *object = movie;
566 return TRUE;
569 /* find last dot or colon */
570 while ((s = strpbrk (end + 1, ".:")) != NULL)
571 end = s;
573 /* variable to use is the part after the last dot or colon */
574 *variable = end + 1;
575 /* look up object for start of path */
576 movie = swfdec_action_lookup_object (cx, path, end);
577 if (movie) {
578 *object = movie;
579 return TRUE;
580 } else {
581 *variable = NULL;
582 return FALSE;
586 static void
587 swfdec_action_get_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
589 SwfdecAsValue *val;
590 const char *s;
591 SwfdecAsObject *object;
593 val = swfdec_as_stack_peek (cx, 1);
594 s = swfdec_as_value_to_string (cx, val);
595 if (swfdec_action_get_movie_by_path (cx, s, &object, &s)) {
596 if (object) {
597 if (s) {
598 swfdec_as_object_get_variable (object, swfdec_as_context_get_string (cx, s), val);
599 } else {
600 SWFDEC_AS_VALUE_SET_OBJECT (val, object);
602 } else {
603 swfdec_as_frame_get_variable (cx->frame, swfdec_as_context_get_string (cx, s), val);
605 } else {
606 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
607 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
608 SWFDEC_WARNING ("no variable named %s", s);
609 #endif
613 static void
614 swfdec_action_set_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
616 const char *s;
618 s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
619 swfdec_as_context_eval_set (cx, NULL, s, swfdec_as_stack_peek (cx, 1));
620 swfdec_as_stack_pop_n (cx, 2);
623 static const char *
624 swfdec_as_interpret_eval (SwfdecAsContext *cx, SwfdecAsObject *obj,
625 SwfdecAsValue *val)
627 if (SWFDEC_AS_VALUE_IS_STRING (val)) {
628 const char *s = SWFDEC_AS_VALUE_GET_STRING (val);
629 if (s != SWFDEC_AS_STR_EMPTY) {
630 swfdec_as_context_eval (cx, obj, s, val);
631 return s;
634 if (obj != NULL)
635 SWFDEC_AS_VALUE_SET_OBJECT (val, obj);
636 else
637 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
638 return SWFDEC_AS_STR_EMPTY;
641 /* FIXME: this sucks */
642 extern struct {
643 gboolean needs_movie;
644 const char * name; /* GC'd */
645 void (* get) (SwfdecMovie *movie, SwfdecAsValue *ret);
646 void (* set) (SwfdecMovie *movie, const SwfdecAsValue *val);
647 } swfdec_movieclip_props[];
648 static void
649 swfdec_action_get_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
651 SwfdecAsValue *val;
652 SwfdecAsObject *obj;
653 guint id;
655 id = swfdec_as_value_to_integer (cx, swfdec_as_stack_pop (cx));
656 if (id > (cx->version > 4 ? 21 : 18)) {
657 SWFDEC_WARNING ("trying to SetProperty %u, not allowed", id);
658 goto out;
660 val = swfdec_as_stack_peek (cx, 1);
661 swfdec_as_interpret_eval (cx, NULL, val);
662 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val)) {
663 obj = cx->frame->target;
664 } else if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
665 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
666 } else {
667 SWFDEC_WARNING ("not an object, can't GetProperty");
668 goto out;
670 swfdec_as_object_get_variable (obj, swfdec_movieclip_props[id].name,
671 swfdec_as_stack_peek (cx, 1));
672 return;
674 out:
675 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
678 static void
679 swfdec_action_set_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
681 SwfdecAsValue *val;
682 SwfdecAsObject *obj;
683 guint id;
685 id = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
686 if (id > (cx->version > 4 ? 21 : 18)) {
687 SWFDEC_WARNING ("trying to SetProperty %u, not allowed", id);
688 goto out;
690 val = swfdec_as_stack_peek (cx, 3);
691 swfdec_as_interpret_eval (cx, NULL, val);
692 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val)) {
693 obj = cx->frame->target;
694 } else if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
695 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
696 } else {
697 SWFDEC_WARNING ("not an object, can't get SetProperty");
698 goto out;
700 swfdec_as_object_set_variable (obj, swfdec_movieclip_props[id].name,
701 swfdec_as_stack_peek (cx, 1));
702 out:
703 swfdec_as_stack_pop_n (cx, 3);
706 static void
707 swfdec_action_get_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
709 SwfdecAsObject *object = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 2));
710 if (object) {
711 const char *name;
712 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
713 swfdec_as_object_get_variable (object, name, swfdec_as_stack_peek (cx, 2));
714 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
715 if (SWFDEC_AS_VALUE_IS_UNDEFINED (swfdec_as_stack_peek (cx, 2))) {
716 SWFDEC_WARNING ("no variable named %s:%s", G_OBJECT_TYPE_NAME (object), name);
718 #endif
719 } else {
720 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
722 swfdec_as_stack_pop (cx);
725 static void
726 swfdec_action_set_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
728 if (SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 3))) {
729 const char *name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
730 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 3)),
731 name, swfdec_as_stack_peek (cx, 1));
733 swfdec_as_stack_pop_n (cx, 3);
736 static void
737 swfdec_action_trace (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
739 SwfdecAsValue *val;
740 const char *s;
742 val = swfdec_as_stack_peek (cx, 1);
743 if (val->type == SWFDEC_AS_TYPE_UNDEFINED) {
744 s = SWFDEC_AS_STR_undefined;
745 } else if (val->type == SWFDEC_AS_TYPE_OBJECT &&
746 SWFDEC_IS_AS_STRING (swfdec_as_value_to_object (cx, val))) {
747 s = SWFDEC_AS_STRING (swfdec_as_value_to_object (cx, val))->string;
748 } else {
749 s = swfdec_as_value_to_string (cx, val);
751 swfdec_as_stack_pop (cx);
752 g_signal_emit_by_name (cx, "trace", s);
755 /* stack looks like this: [ function, this, arg1, arg2, ... ] */
756 /* stack must be at least 2 elements big */
757 static gboolean
758 swfdec_action_call (SwfdecAsContext *cx, guint n_args)
760 SwfdecAsFunction *fun;
761 SwfdecAsObject *thisp;
763 if (!SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 1)))
764 goto error;
765 fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 1));
766 if (!SWFDEC_IS_AS_FUNCTION (fun))
767 goto error;
768 if (!SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 2))) {
769 thisp = NULL;
770 } else {
771 thisp = SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 2));
773 swfdec_as_stack_pop_n (cx, 2);
774 /* sanitize argument count */
775 if (n_args >= swfdec_as_stack_get_size (cx))
776 n_args = swfdec_as_stack_get_size (cx);
777 swfdec_as_function_call (fun, thisp, n_args, NULL, NULL);
778 if (SWFDEC_IS_AS_SUPER (fun)) {
779 SWFDEC_LOG ("replacing super object on frame");
780 swfdec_as_super_replace (SWFDEC_AS_SUPER (fun), NULL);
782 return TRUE;
784 error:
785 n_args += 2;
786 if (n_args > swfdec_as_stack_get_size (cx))
787 n_args = swfdec_as_stack_get_size (cx);
788 swfdec_as_stack_pop_n (cx, n_args);
789 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
790 return FALSE;
793 static void
794 swfdec_action_call_function (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
796 SwfdecAsFrame *frame = cx->frame;
797 SwfdecAsObject *obj;
798 guint n_args;
799 const char *name;
800 SwfdecAsValue *fun, *thisp;
802 swfdec_as_stack_ensure_size (cx, 2);
803 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
804 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
805 thisp = swfdec_as_stack_peek (cx, 2);
806 fun = swfdec_as_stack_peek (cx, 1);
807 obj = swfdec_as_frame_get_variable (frame, name, fun);
808 if (obj) {
809 SWFDEC_AS_VALUE_SET_OBJECT (thisp, obj);
810 } else {
811 SWFDEC_AS_VALUE_SET_NULL (thisp);
812 SWFDEC_AS_VALUE_SET_UNDEFINED (fun);
814 if (!swfdec_action_call (cx, n_args)) {
815 SWFDEC_WARNING ("no function named %s", name);
819 static void
820 swfdec_action_call_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
822 SwfdecAsFrame *frame = cx->frame;
823 SwfdecAsValue *val;
824 SwfdecAsObject *obj;
825 guint n_args;
826 const char *name = NULL;
828 swfdec_as_stack_ensure_size (cx, 3);
829 obj = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 2));
830 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 3));
831 val = swfdec_as_stack_peek (cx, 1);
832 /* FIXME: this is a hack for constructors calling super - is this correct? */
833 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val)) {
834 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
836 if (obj) {
837 if (SWFDEC_AS_VALUE_IS_STRING (val) &&
838 SWFDEC_AS_VALUE_GET_STRING (val) == SWFDEC_AS_STR_EMPTY) {
839 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 3));
840 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 2), obj);
841 } else {
842 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 3), obj);
843 name = swfdec_as_value_to_string (cx, val);
844 swfdec_as_object_get_variable (obj, name, swfdec_as_stack_peek (cx, 2));
846 } else {
847 if (SWFDEC_AS_VALUE_IS_STRING (val))
848 name = SWFDEC_AS_VALUE_GET_STRING (val);
849 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_peek (cx, 3));
850 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
852 swfdec_as_stack_pop (cx);
853 if (swfdec_action_call (cx, n_args)) {
854 /* setup super to point to the right prototype */
855 frame = cx->frame;
856 if (SWFDEC_IS_AS_SUPER (obj)) {
857 swfdec_as_super_replace (SWFDEC_AS_SUPER (obj), name);
858 } else if (frame->super) {
859 SwfdecAsSuper *super = SWFDEC_AS_SUPER (frame->super);
860 if (name &&
861 cx->version > 6 &&
862 swfdec_as_object_get_variable_and_flags (frame->thisp,
863 name, NULL, NULL, &super->object) &&
864 super->object == frame->thisp) {
865 super->object = super->object->prototype;
868 } else {
869 SWFDEC_WARNING ("no function named %s on object %s", name ? name : "unknown", obj ? G_OBJECT_TYPE_NAME(obj) : "unknown");
873 static void
874 swfdec_action_pop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
876 swfdec_as_stack_pop (cx);
879 static void
880 swfdec_action_binary (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
882 double l, r;
884 r = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
885 l = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
886 switch (action) {
887 case SWFDEC_AS_ACTION_ADD:
888 l = l + r;
889 break;
890 case SWFDEC_AS_ACTION_SUBTRACT:
891 l = l - r;
892 break;
893 case SWFDEC_AS_ACTION_MULTIPLY:
894 l = l * r;
895 break;
896 case SWFDEC_AS_ACTION_DIVIDE:
897 if (cx->version < 5) {
898 if (r == 0) {
899 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), SWFDEC_AS_STR__ERROR_);
900 return;
903 if (r == 0) {
904 if (l > 0)
905 l = INFINITY;
906 else if (l < 0)
907 l = -INFINITY;
908 else
909 l = NAN;
910 } else {
911 l = l / r;
913 break;
914 default:
915 g_assert_not_reached ();
916 break;
918 swfdec_as_stack_pop (cx);
919 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), l);
922 static void
923 swfdec_action_add2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
925 SwfdecAsValue *rval, *lval, rtmp, ltmp;
927 rval = swfdec_as_stack_peek (cx, 1);
928 lval = swfdec_as_stack_peek (cx, 2);
929 rtmp = *rval;
930 ltmp = *lval;
931 swfdec_as_value_to_primitive (&rtmp);
932 if (!SWFDEC_AS_VALUE_IS_OBJECT (&rtmp) || SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&rtmp)))
933 rval = &rtmp;
934 swfdec_as_value_to_primitive (&ltmp);
935 if (!SWFDEC_AS_VALUE_IS_OBJECT (&ltmp) || SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&ltmp)))
936 lval = &ltmp;
938 if (SWFDEC_AS_VALUE_IS_STRING (lval) || SWFDEC_AS_VALUE_IS_STRING (rval)) {
939 const char *lstr, *rstr;
940 lstr = swfdec_as_value_to_string (cx, lval);
941 rstr = swfdec_as_value_to_string (cx, rval);
942 lstr = swfdec_as_str_concat (cx, lstr, rstr);
943 swfdec_as_stack_pop (cx);
944 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), lstr);
945 } else {
946 double d, d2;
947 d = swfdec_as_value_to_number (cx, lval);
948 d2 = swfdec_as_value_to_number (cx, rval);
949 d += d2;
950 swfdec_as_stack_pop (cx);
951 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), d);
955 static void
956 swfdec_action_new_comparison (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
958 SwfdecAsValue *lval, *rval;
959 double l, r;
961 rval = swfdec_as_stack_peek (cx, 1);
962 lval = swfdec_as_stack_peek (cx, 2);
964 /* swap if we do a greater comparison */
965 if (action == SWFDEC_AS_ACTION_GREATER) {
966 SwfdecAsValue *tmp = lval;
967 lval = rval;
968 rval = tmp;
970 /* comparison with object is always false */
971 swfdec_as_value_to_primitive (lval);
972 if (SWFDEC_AS_VALUE_IS_OBJECT (lval) &&
973 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (lval))) {
974 swfdec_as_stack_pop (cx);
975 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
976 return;
978 /* same for the rval */
979 swfdec_as_value_to_primitive (rval);
980 if (SWFDEC_AS_VALUE_IS_OBJECT (rval) &&
981 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (rval))) {
982 swfdec_as_stack_pop (cx);
983 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
984 return;
986 /* movieclips are not objects, but they evaluate to NaN, so we can handle them here */
987 if (SWFDEC_AS_VALUE_IS_OBJECT (rval) ||
988 SWFDEC_AS_VALUE_IS_OBJECT (lval)) {
989 swfdec_as_stack_pop (cx);
990 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
991 return;
993 /* if both are strings, compare strings */
994 if (SWFDEC_AS_VALUE_IS_STRING (rval) &&
995 SWFDEC_AS_VALUE_IS_STRING (lval)) {
996 const char *ls = SWFDEC_AS_VALUE_GET_STRING (lval);
997 const char *rs = SWFDEC_AS_VALUE_GET_STRING (rval);
998 int cmp;
999 if (ls == SWFDEC_AS_STR_EMPTY) {
1000 cmp = rs == SWFDEC_AS_STR_EMPTY ? 0 : 1;
1001 } else if (rs == SWFDEC_AS_STR_EMPTY) {
1002 cmp = -1;
1003 } else {
1004 cmp = strcmp (ls, rs);
1006 swfdec_as_stack_pop (cx);
1007 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cmp < 0);
1008 return;
1010 /* convert to numbers and compare those */
1011 l = swfdec_as_value_to_number (cx, lval);
1012 r = swfdec_as_value_to_number (cx, rval);
1013 swfdec_as_stack_pop (cx);
1014 /* NaN results in undefined */
1015 if (isnan (l) || isnan (r)) {
1016 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1017 return;
1019 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), l < r);
1022 static void
1023 swfdec_action_not_4 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1025 double d;
1027 d = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
1028 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), d == 0 ? 1 : 0);
1031 static void
1032 swfdec_action_not_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1034 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1),
1035 !swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1)));
1038 static void
1039 swfdec_action_jump (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1041 if (len != 2) {
1042 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
1043 return;
1045 cx->frame->pc += 5 + GINT16_FROM_LE (*((gint16*) data));
1048 static void
1049 swfdec_action_if (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1051 if (len != 2) {
1052 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
1053 return;
1055 if (swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1)))
1056 cx->frame->pc += 5 + GINT16_FROM_LE (*((gint16*) data));
1057 swfdec_as_stack_pop (cx);
1060 static void
1061 swfdec_action_decrement (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1063 SwfdecAsValue *val;
1065 val = swfdec_as_stack_peek (cx, 1);
1066 SWFDEC_AS_VALUE_SET_NUMBER (val, swfdec_as_value_to_number (cx, val) - 1);
1069 static void
1070 swfdec_action_increment (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1072 SwfdecAsValue *val;
1074 val = swfdec_as_stack_peek (cx, 1);
1075 SWFDEC_AS_VALUE_SET_NUMBER (val, swfdec_as_value_to_number (cx, val) + 1);
1078 static void
1079 swfdec_action_get_url (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1081 SwfdecBits bits;
1082 char *url, *target;
1084 swfdec_bits_init_data (&bits, data, len);
1085 url = swfdec_bits_get_string_with_version (&bits, cx->version);
1086 target = swfdec_bits_get_string_with_version (&bits, cx->version);
1087 if (url == NULL || target == NULL) {
1088 SWFDEC_ERROR ("not enough data in GetURL");
1089 g_free (url);
1090 g_free (target);
1091 return;
1093 if (swfdec_bits_left (&bits)) {
1094 SWFDEC_WARNING ("leftover bytes in GetURL action");
1096 if (SWFDEC_IS_MOVIE (cx->frame->target))
1097 swfdec_movie_load (SWFDEC_MOVIE (cx->frame->target), url, target,
1098 SWFDEC_LOADER_REQUEST_DEFAULT, NULL, 0);
1099 else
1100 SWFDEC_WARNING ("no movie to load");
1101 g_free (url);
1102 g_free (target);
1105 static void
1106 swfdec_action_get_url2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1108 const char *target, *url;
1109 guint method;
1111 if (len != 1) {
1112 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
1113 return;
1116 target = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1117 url = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1118 method = data[0] & 3;
1120 if (method == 3) {
1121 SWFDEC_ERROR ("GetURL method 3 invalid");
1122 method = 0;
1125 if (data[0] & 128 && data[0] & 64) {
1126 if (SWFDEC_IS_MOVIE (cx->frame->target)) {
1127 swfdec_movie_load_variables (SWFDEC_MOVIE (cx->frame->target), url,
1128 target, method);
1129 } else {
1130 SWFDEC_WARNING ("no movie to load");
1132 } else if (data[0] & 128) {
1133 SWFDEC_FIXME ("LoadVariables without LoadTarget?");
1134 } else {
1135 if (data[0] & 64)
1136 SWFDEC_FIXME ("implement LoadTarget");
1137 if (SWFDEC_IS_MOVIE (cx->frame->target)) {
1138 swfdec_movie_load (SWFDEC_MOVIE (cx->frame->target), url, target, method,
1139 NULL, 0);
1140 } else {
1141 SWFDEC_WARNING ("no movie to load");
1145 swfdec_as_stack_pop_n (cx, 2);
1148 static void
1149 swfdec_action_string_add (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1151 const char *lval, *rval;
1153 rval = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1154 lval = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1155 lval = swfdec_as_str_concat (cx, lval, rval);
1156 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 2), lval);
1157 swfdec_as_stack_pop (cx);
1160 static void
1161 swfdec_action_push_duplicate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1163 *swfdec_as_stack_push (cx) = *swfdec_as_stack_peek (cx, 1);
1166 static void
1167 swfdec_action_random_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1169 gint32 max;
1170 SwfdecAsValue *val;
1172 val = swfdec_as_stack_peek (cx, 1);
1173 max = swfdec_as_value_to_integer (cx, val);
1175 if (max <= 0)
1176 SWFDEC_AS_VALUE_SET_NUMBER (val, 0);
1177 else
1178 SWFDEC_AS_VALUE_SET_NUMBER (val, g_rand_int_range (cx->rand, 0, max));
1181 static void
1182 swfdec_action_old_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1184 double l, r;
1185 gboolean cond;
1187 l = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
1188 r = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
1189 switch (action) {
1190 case SWFDEC_AS_ACTION_EQUALS:
1191 cond = l == r;
1192 break;
1193 case SWFDEC_AS_ACTION_LESS:
1194 cond = l < r;
1195 break;
1196 default:
1197 g_assert_not_reached ();
1198 return;
1200 swfdec_as_stack_pop (cx);
1201 if (cx->version < 5) {
1202 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), cond ? 1 : 0);
1203 } else {
1204 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1208 static void
1209 swfdec_action_string_length (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1211 const char *s;
1212 SwfdecAsValue *v;
1214 v = swfdec_as_stack_peek (cx, 1);
1215 s = swfdec_as_value_to_string (cx, v);
1216 SWFDEC_AS_VALUE_SET_INT (v, g_utf8_strlen (s, -1));
1219 static void
1220 swfdec_action_string_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1222 const char *l, *r;
1223 gboolean cond;
1225 r = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1226 l = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1227 switch (action) {
1228 case SWFDEC_AS_ACTION_STRING_EQUALS:
1229 cond = l == r;
1230 break;
1231 case SWFDEC_AS_ACTION_STRING_LESS:
1232 cond = strcmp (l, r) < 0;
1233 break;
1234 default:
1235 g_assert_not_reached ();
1236 break;
1238 swfdec_as_stack_pop (cx);
1239 if (cx->version < 5) {
1240 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), cond ? 1 : 0);
1241 } else {
1242 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1246 static void
1247 swfdec_action_equals2_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1249 SwfdecAsValue *rval, *lval;
1250 SwfdecAsValue rtmp, ltmp;
1251 SwfdecAsValueType ltype, rtype;
1252 double l, r;
1253 gboolean cond;
1255 rval = swfdec_as_stack_peek (cx, 1);
1256 lval = swfdec_as_stack_peek (cx, 2);
1257 rtmp = *rval;
1258 ltmp = *lval;
1259 swfdec_as_value_to_primitive (&rtmp);
1260 swfdec_as_value_to_primitive (&ltmp);
1261 ltype = ltmp.type;
1262 rtype = rtmp.type;
1264 /* get objects compared */
1265 if (ltype == SWFDEC_AS_TYPE_OBJECT && rtype == SWFDEC_AS_TYPE_OBJECT) {
1266 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (&ltmp);
1267 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (&rtmp);
1269 if (!SWFDEC_IS_MOVIE (lo))
1270 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1271 if (!SWFDEC_IS_MOVIE (ro))
1272 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1274 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1275 /* do nothing */
1276 } else if (SWFDEC_IS_MOVIE (lo)) {
1277 swfdec_as_value_to_primitive (rval);
1278 rtype = rval->type;
1279 if (rtype != SWFDEC_AS_TYPE_OBJECT) {
1280 cond = FALSE;
1281 goto out;
1283 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1284 } else if (SWFDEC_IS_MOVIE (ro)) {
1285 swfdec_as_value_to_primitive (lval);
1286 ltype = lval->type;
1287 if (ltype != SWFDEC_AS_TYPE_OBJECT) {
1288 cond = FALSE;
1289 goto out;
1291 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1293 cond = lo == ro;
1294 goto out;
1297 /* compare strings */
1298 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1299 /* FIXME: flash 5 case insensitive? */
1300 cond = SWFDEC_AS_VALUE_GET_STRING (&ltmp) == SWFDEC_AS_VALUE_GET_STRING (&rtmp);
1301 goto out;
1304 /* convert to numbers */
1305 if (SWFDEC_AS_VALUE_IS_OBJECT (&ltmp) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&ltmp))) {
1306 l = swfdec_as_value_to_number (cx, lval);
1307 } else {
1308 l = swfdec_as_value_to_number (cx, &ltmp);
1310 if (SWFDEC_AS_VALUE_IS_OBJECT (&rtmp) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&rtmp))) {
1311 r = swfdec_as_value_to_number (cx, rval);
1312 } else {
1313 r = swfdec_as_value_to_number (cx, &rtmp);
1316 /* get rid of undefined and null */
1317 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1318 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1319 goto out;
1320 } else if (cond) {
1321 cond = FALSE;
1322 goto out;
1325 /* else compare as numbers */
1326 if (isnan (l) && isnan (r))
1327 cond = ltype == rtype;
1328 else
1329 cond = l == r;
1331 out:
1332 swfdec_as_stack_pop (cx);
1333 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1336 static void
1337 swfdec_action_equals2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1339 SwfdecAsValue *rval, *lval;
1340 SwfdecAsValueType ltype, rtype;
1341 double l, r;
1342 gboolean cond;
1344 rval = swfdec_as_stack_peek (cx, 1);
1345 lval = swfdec_as_stack_peek (cx, 2);
1346 ltype = lval->type;
1347 rtype = rval->type;
1349 /* get objects compared */
1350 if (ltype == SWFDEC_AS_TYPE_OBJECT && rtype == SWFDEC_AS_TYPE_OBJECT) {
1351 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1352 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1354 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1355 /* do nothing */
1356 } else if (SWFDEC_IS_MOVIE (lo)) {
1357 swfdec_as_value_to_primitive (rval);
1358 rtype = rval->type;
1359 if (rtype != SWFDEC_AS_TYPE_OBJECT) {
1360 cond = FALSE;
1361 goto out;
1363 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1364 } else if (SWFDEC_IS_MOVIE (ro)) {
1365 swfdec_as_value_to_primitive (lval);
1366 ltype = lval->type;
1367 if (ltype != SWFDEC_AS_TYPE_OBJECT) {
1368 cond = FALSE;
1369 goto out;
1371 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1373 cond = lo == ro;
1374 goto out;
1377 /* if one of the values is an object, call valueOf.
1378 * If it's still an object, return FALSE */
1379 swfdec_as_value_to_primitive (lval);
1380 ltype = lval->type;
1381 if (ltype == SWFDEC_AS_TYPE_OBJECT) {
1382 cond = FALSE;
1383 goto out;
1385 swfdec_as_value_to_primitive (rval);
1386 rtype = rval->type;
1387 if (rtype == SWFDEC_AS_TYPE_OBJECT) {
1388 cond = FALSE;
1389 goto out;
1391 /* now we have a comparison without objects */
1393 /* get rid of undefined and null */
1394 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1395 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1396 goto out;
1397 } else if (cond) {
1398 cond = FALSE;
1399 goto out;
1402 /* compare strings */
1403 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1404 /* FIXME: flash 5 case insensitive? */
1405 cond = SWFDEC_AS_VALUE_GET_STRING (lval) == SWFDEC_AS_VALUE_GET_STRING (rval);
1406 goto out;
1409 /* else compare as numbers */
1410 l = swfdec_as_value_to_number (cx, lval);
1411 r = swfdec_as_value_to_number (cx, rval);
1413 if (isnan (l) && isnan (r))
1414 cond = ltype == rtype;
1415 else
1416 cond = l == r;
1418 out:
1419 swfdec_as_stack_pop (cx);
1420 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1423 static void
1424 swfdec_action_strict_equals (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1426 SwfdecAsValue *rval, *lval;
1427 gboolean cond;
1429 rval = swfdec_as_stack_peek (cx, 1);
1430 lval = swfdec_as_stack_peek (cx, 2);
1432 if (rval->type != lval->type) {
1433 cond = FALSE;
1434 } else {
1435 switch (rval->type) {
1436 case SWFDEC_AS_TYPE_UNDEFINED:
1437 case SWFDEC_AS_TYPE_NULL:
1438 cond = TRUE;
1439 break;
1440 case SWFDEC_AS_TYPE_BOOLEAN:
1441 cond = SWFDEC_AS_VALUE_GET_BOOLEAN (rval) == SWFDEC_AS_VALUE_GET_BOOLEAN (lval);
1442 break;
1443 case SWFDEC_AS_TYPE_NUMBER:
1445 double l, r;
1446 r = SWFDEC_AS_VALUE_GET_NUMBER (rval);
1447 l = SWFDEC_AS_VALUE_GET_NUMBER (lval);
1448 cond = (l == r) || (isnan (l) && isnan (r));
1450 break;
1451 case SWFDEC_AS_TYPE_STRING:
1452 cond = SWFDEC_AS_VALUE_GET_STRING (rval) == SWFDEC_AS_VALUE_GET_STRING (lval);
1453 break;
1454 case SWFDEC_AS_TYPE_OBJECT:
1455 cond = SWFDEC_AS_VALUE_GET_OBJECT (rval) == SWFDEC_AS_VALUE_GET_OBJECT (lval);
1456 break;
1457 default:
1458 g_assert_not_reached ();
1459 cond = FALSE;
1463 swfdec_as_stack_pop (cx);
1464 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1467 static void
1468 swfdec_action_set_target (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1470 if (!memchr (data, 0, len)) {
1471 SWFDEC_ERROR ("SetTarget action does not specify a string");
1472 return;
1474 if (*data == '\0') {
1475 swfdec_as_frame_set_target (cx->frame, NULL);
1476 } else {
1477 SwfdecAsValue target;
1478 swfdec_as_context_eval (cx, NULL, (const char *) data, &target);
1479 if (!SWFDEC_AS_VALUE_IS_OBJECT (&target)) {
1480 SWFDEC_WARNING ("target is not an object");
1481 return;
1483 /* FIXME: allow non-movieclips as targets? */
1484 swfdec_as_frame_set_target (cx->frame, SWFDEC_AS_VALUE_GET_OBJECT (&target));
1488 static void
1489 swfdec_action_set_target2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1491 SwfdecAsValue *val;
1492 val = swfdec_as_stack_peek (cx, 1);
1493 if (!SWFDEC_AS_VALUE_IS_OBJECT (val)) {
1494 SWFDEC_WARNING ("target is not an object");
1495 } else {
1496 /* FIXME: allow non-movieclips as targets? */
1497 swfdec_as_frame_set_target (cx->frame, SWFDEC_AS_VALUE_GET_OBJECT (val));
1499 swfdec_as_stack_pop (cx);
1502 static void
1503 swfdec_action_start_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1505 SwfdecRect rect, *rectp = NULL;
1506 SwfdecMovie *movie;
1507 gboolean center;
1508 guint stack_size = 3;
1510 swfdec_as_stack_ensure_size (cx, 3);
1511 if (swfdec_as_interpret_eval (cx, NULL, swfdec_as_stack_peek (cx, 1)) == SWFDEC_AS_STR_EMPTY) {
1512 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 1), cx->frame->target);
1514 center = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 2));
1515 if (swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 3))) {
1516 swfdec_as_stack_ensure_size (cx, 7);
1517 rect.x0 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 7));
1518 rect.y0 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 6));
1519 rect.x1 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 5));
1520 rect.y1 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 4));
1521 swfdec_rect_scale (&rect, &rect, SWFDEC_TWIPS_SCALE_FACTOR);
1522 stack_size = 7;
1523 rectp = &rect;
1525 if (SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 1)) &&
1526 SWFDEC_IS_MOVIE (movie = (SwfdecMovie *) SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 1)))) {
1527 swfdec_player_set_drag_movie (SWFDEC_PLAYER (cx), movie, center, &rect);
1528 } else {
1529 SWFDEC_ERROR ("startDrag on something not a Movie");
1531 swfdec_as_stack_pop_n (cx, stack_size);
1534 static void
1535 swfdec_action_end_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1537 if (SWFDEC_IS_PLAYER (cx)) {
1538 swfdec_player_set_drag_movie (SWFDEC_PLAYER (cx), NULL, FALSE, NULL);
1539 } else {
1540 SWFDEC_WARNING ("can't end a drag on non-players");
1544 static void
1545 swfdec_action_stop_sounds (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1547 if (SWFDEC_IS_PLAYER (cx)) {
1548 swfdec_player_stop_all_sounds (SWFDEC_PLAYER (cx));
1552 static void
1553 swfdec_action_new_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1555 SwfdecAsValue *constructor;
1556 SwfdecAsFunction *fun;
1557 guint n_args;
1558 const char *name;
1560 swfdec_as_stack_ensure_size (cx, 2);
1561 constructor = swfdec_as_stack_peek (cx, 1);
1562 name = swfdec_as_interpret_eval (cx, NULL, constructor);
1563 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1564 n_args = MIN (swfdec_as_stack_get_size (cx) - 2, n_args);
1565 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor) ||
1566 !SWFDEC_IS_AS_FUNCTION (fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (constructor))) {
1567 SWFDEC_WARNING ("%s is not a constructor", name);
1568 goto fail;
1571 swfdec_as_stack_pop_n (cx, 2);
1572 swfdec_as_object_create (fun, n_args, NULL);
1573 return;
1575 fail:
1576 swfdec_as_stack_pop_n (cx, n_args + 1);
1577 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1580 static void
1581 swfdec_action_new_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1583 SwfdecAsValue *constructor;
1584 SwfdecAsFunction *fun;
1585 guint n_args;
1586 const char *name;
1588 swfdec_as_stack_ensure_size (cx, 3);
1589 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1591 constructor = swfdec_as_stack_peek (cx, 2);
1592 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 3));
1593 n_args = MIN (swfdec_as_stack_get_size (cx) - 3, n_args);
1594 if (name != SWFDEC_AS_STR_EMPTY) {
1595 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor)) {
1596 SWFDEC_WARNING ("NewMethod called without an object to get variable %s from", name);
1597 goto fail;
1599 swfdec_as_object_get_variable (SWFDEC_AS_VALUE_GET_OBJECT (constructor),
1600 name, constructor);
1602 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor) ||
1603 !SWFDEC_IS_AS_FUNCTION (fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (constructor))) {
1604 SWFDEC_WARNING ("%s is not a constructor", name);
1605 goto fail;
1608 swfdec_as_stack_pop_n (cx, 3);
1609 swfdec_as_object_create (fun, n_args, NULL);
1610 return;
1612 fail:
1613 swfdec_as_stack_pop_n (cx, n_args + 2);
1614 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1617 static void
1618 swfdec_action_init_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1620 SwfdecAsObject *object;
1621 guint i, n_args, size;
1623 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1624 swfdec_as_stack_pop (cx);
1625 if (n_args * 2 > swfdec_as_stack_get_size (cx)) {
1626 size = swfdec_as_stack_get_size (cx);
1627 SWFDEC_FIXME ("InitObject action with too small stack, help!");
1628 n_args = size / 2;
1629 size &= 1;
1630 } else {
1631 size = 0;
1634 object = swfdec_as_object_new (cx);
1635 if (object == NULL)
1636 return;
1637 for (i = 0; i < n_args; i++) {
1638 const char *s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1639 swfdec_as_object_set_variable (object, s, swfdec_as_stack_peek (cx, 1));
1640 swfdec_as_stack_pop_n (cx, 2);
1642 swfdec_as_stack_pop_n (cx, size);
1643 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), object);
1646 static void
1647 swfdec_action_init_array (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1649 int i, n;
1650 SwfdecAsObject *array;
1652 swfdec_as_stack_ensure_size (cx, 1);
1653 n = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1654 swfdec_as_stack_pop (cx);
1655 array = swfdec_as_array_new (cx);
1656 if (array == NULL)
1657 return;
1658 /* NB: we can't increase the stack here, as the number can easily be MAXINT */
1659 for (i = 0; i < n && swfdec_as_stack_get_size (cx) > 0; i++) {
1660 swfdec_as_stack_ensure_size (cx, 1);
1661 swfdec_as_array_push (SWFDEC_AS_ARRAY (array), swfdec_as_stack_pop (cx));
1663 if (i != n) {
1664 SwfdecAsValue val;
1665 SWFDEC_AS_VALUE_SET_INT (&val, i);
1666 swfdec_as_object_set_variable (array, SWFDEC_AS_STR_length, &val);
1668 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), array);
1671 static void
1672 swfdec_action_define_function (SwfdecAsContext *cx, guint action,
1673 const guint8 *data, guint len)
1675 char *function_name;
1676 const char *name = NULL;
1677 guint i, n_args, size, n_registers;
1678 SwfdecBuffer *buffer;
1679 SwfdecBits bits;
1680 SwfdecAsFunction *fun;
1681 SwfdecAsFrame *frame;
1682 SwfdecScript *script;
1683 guint flags = 0;
1684 SwfdecScriptArgument *args;
1685 gboolean v2 = (action == 0x8e);
1687 frame = cx->frame;
1688 swfdec_bits_init_data (&bits, data, len);
1689 function_name = swfdec_bits_get_string_with_version (&bits, cx->version);
1690 if (function_name == NULL) {
1691 SWFDEC_ERROR ("could not parse function name");
1692 return;
1694 n_args = swfdec_bits_get_u16 (&bits);
1695 if (v2) {
1696 n_registers = swfdec_bits_get_u8 (&bits);
1697 if (n_registers == 0)
1698 n_registers = 4;
1699 flags = swfdec_bits_get_u16 (&bits);
1700 } else {
1701 n_registers = 5;
1703 if (n_args) {
1704 args = g_new0 (SwfdecScriptArgument, n_args);
1705 for (i = 0; i < n_args && swfdec_bits_left (&bits); i++) {
1706 if (v2) {
1707 args[i].preload = swfdec_bits_get_u8 (&bits);
1708 if (args[i].preload && args[i].preload >= n_registers) {
1709 SWFDEC_ERROR ("argument %u cannot be preloaded into register %u out of %u",
1710 i, args[i].preload, n_registers);
1711 /* FIXME: figure out correct error handling here */
1712 args[i].preload = 0;
1715 args[i].name = swfdec_bits_get_string_with_version (&bits, cx->version);
1716 if (args[i].name == NULL || args[i].name == '\0') {
1717 SWFDEC_ERROR ("empty argument name not allowed");
1718 g_free (args);
1719 return;
1721 /* FIXME: check duplicate arguments */
1723 } else {
1724 args = NULL;
1726 size = swfdec_bits_get_u16 (&bits);
1727 /* check the script can be created */
1728 if (frame->script->buffer->data + frame->script->buffer->length < frame->pc + 3 + len + size) {
1729 SWFDEC_ERROR ("size of function is too big");
1730 g_free (args);
1731 g_free (function_name);
1732 return;
1734 /* create the script */
1735 buffer = swfdec_buffer_new_subbuffer (frame->script->buffer,
1736 frame->pc + 3 + len - frame->script->buffer->data, size);
1737 swfdec_bits_init (&bits, buffer);
1738 if (*function_name) {
1739 name = function_name;
1740 } else if (swfdec_as_stack_get_size (cx) > 0) {
1741 /* This is kind of a hack that uses a feature of the Adobe compiler:
1742 * foo = function () {} is compiled as these actions:
1743 * Push "foo", DefineFunction, SetVariable/SetMember
1744 * With this knowledge we can inspect the topmost stack member, since
1745 * it will contain the name this function will soon be assigned to.
1747 if (SWFDEC_AS_VALUE_IS_STRING (swfdec_as_stack_peek (cx, 1)))
1748 name = SWFDEC_AS_VALUE_GET_STRING (swfdec_as_stack_peek (cx, 1));
1750 if (name == NULL)
1751 name = "unnamed_function";
1752 script = swfdec_script_new_from_bits (&bits, name, cx->version);
1753 swfdec_buffer_unref (buffer);
1754 if (script == NULL) {
1755 SWFDEC_ERROR ("failed to create script");
1756 g_free (args);
1757 g_free (function_name);
1758 return;
1760 if (frame->constant_pool_buffer)
1761 script->constant_pool = swfdec_buffer_ref (frame->constant_pool_buffer);
1762 script->flags = flags;
1763 script->n_registers = n_registers;
1764 script->n_arguments = n_args;
1765 script->arguments = args;
1766 /* see function-scope tests */
1767 if (cx->version > 5) {
1768 /* FIXME: or original target? */
1769 fun = swfdec_as_script_function_new (frame->target, frame->scope_chain, script);
1770 } else {
1771 fun = swfdec_as_script_function_new (frame->target, NULL, script);
1773 if (fun == NULL)
1774 return;
1775 /* attach the function */
1776 if (*function_name == '\0') {
1777 swfdec_as_stack_ensure_free (cx, 1);
1778 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), SWFDEC_AS_OBJECT (fun));
1779 } else {
1780 SwfdecAsValue funval;
1781 /* FIXME: really varobj? Not eval or sth like that? */
1782 name = swfdec_as_context_get_string (cx, function_name);
1783 SWFDEC_AS_VALUE_SET_OBJECT (&funval, SWFDEC_AS_OBJECT (fun));
1784 swfdec_as_object_set_variable (frame->target, name, &funval);
1787 /* update current context */
1788 frame->pc += 3 + len + size;
1789 g_free (function_name);
1792 static void
1793 swfdec_action_bitwise (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1795 int a, b;
1797 a = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1798 b = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1800 switch (action) {
1801 case 0x60:
1802 a = (int) (a & b);
1803 break;
1804 case 0x61:
1805 a = (int) (a | b);
1806 break;
1807 case 0x62:
1808 a = (int) (a ^ b);
1809 break;
1810 default:
1811 g_assert_not_reached ();
1812 break;
1815 swfdec_as_stack_pop (cx);
1816 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_peek (cx, 1), a);
1819 static void
1820 swfdec_action_shift (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1822 int amount, value;
1824 amount = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1825 amount &= 31;
1826 value = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1828 switch (action) {
1829 case 0x63:
1830 value = value << amount;
1831 break;
1832 case 0x64:
1833 value = ((gint) value) >> amount;
1834 break;
1835 case 0x65:
1836 value = ((guint) value) >> amount;
1837 break;
1838 default:
1839 g_assert_not_reached ();
1842 swfdec_as_stack_pop (cx);
1843 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_peek (cx, 1), value);
1846 static void
1847 swfdec_action_to_integer (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1849 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
1851 SWFDEC_AS_VALUE_SET_INT (val, swfdec_as_value_to_integer (cx, val));
1854 static void
1855 swfdec_action_target_path (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1857 SwfdecAsValue *val;
1858 SwfdecMovie *movie;
1859 char *s;
1861 val = swfdec_as_stack_peek (cx, 1);
1863 if (!SWFDEC_AS_VALUE_IS_OBJECT (val) ||
1864 !SWFDEC_IS_MOVIE (movie = (SwfdecMovie *) SWFDEC_AS_VALUE_GET_OBJECT (val))) {
1865 SWFDEC_FIXME ("What's the TargetPath for non-movies?");
1866 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
1867 return;
1869 s = swfdec_movie_get_path (movie);
1870 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_give_string (cx, s));
1873 static void
1874 swfdec_action_define_local (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1876 SwfdecAsObject *target;
1877 const char *name;
1879 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1880 if (cx->frame->is_local) {
1881 target = SWFDEC_AS_OBJECT (cx->frame);
1882 } else {
1883 target = cx->frame->target;
1885 swfdec_as_object_set_variable (target, name,
1886 swfdec_as_stack_peek (cx, 1));
1887 swfdec_as_stack_pop_n (cx, 2);
1890 static void
1891 swfdec_action_define_local2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1893 SwfdecAsValue val = { 0, };
1894 SwfdecAsObject *target;
1895 const char *name;
1897 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1898 if (cx->frame->is_local) {
1899 target = SWFDEC_AS_OBJECT (cx->frame);
1900 } else {
1901 target = cx->frame->target;
1903 swfdec_as_object_set_variable (target, name, &val);
1904 swfdec_as_stack_pop (cx);
1907 static void
1908 swfdec_action_return (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1910 swfdec_as_frame_return (cx->frame, swfdec_as_stack_pop (cx));
1913 static void
1914 swfdec_action_delete (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1916 SwfdecAsValue *val;
1917 const char *name;
1918 gboolean success = FALSE;
1920 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1921 val = swfdec_as_stack_peek (cx, 2);
1922 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
1923 success = swfdec_as_object_delete_variable (
1924 SWFDEC_AS_VALUE_GET_OBJECT (val), name) == SWFDEC_AS_DELETE_DELETED;
1926 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
1927 swfdec_as_stack_pop_n (cx, 1);
1930 static void
1931 swfdec_action_delete2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1933 SwfdecAsValue *val;
1934 const char *name;
1935 gboolean success = FALSE;
1937 val = swfdec_as_stack_peek (cx, 1);
1938 name = swfdec_as_value_to_string (cx, val);
1939 success = swfdec_as_frame_delete_variable (cx->frame, name) == SWFDEC_AS_DELETE_DELETED;
1940 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
1943 static void
1944 swfdec_action_store_register (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1946 if (len != 1) {
1947 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
1948 return;
1950 if (!swfdec_action_has_register (cx, *data)) {
1951 SWFDEC_ERROR ("Cannot store into register %u, not enough registers", (guint) *data);
1952 return;
1954 cx->frame->registers[*data] = *swfdec_as_stack_peek (cx, 1);
1957 static void
1958 swfdec_action_modulo (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1960 double x, y;
1962 y = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
1963 x = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
1964 /* yay, we're portable! */
1965 if (y == 0.0) {
1966 x = NAN;
1967 } else {
1968 errno = 0;
1969 x = fmod (x, y);
1970 if (errno != 0) {
1971 SWFDEC_FIXME ("errno set after fmod");
1974 swfdec_as_stack_pop (cx);
1975 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), x);
1978 static void
1979 swfdec_action_swap (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1981 swfdec_as_stack_swap (cx, 1, 2);
1984 static void
1985 swfdec_action_to_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1987 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1),
1988 swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1)));
1991 static void
1992 swfdec_action_to_string (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1994 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1),
1995 swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1)));
1998 static void
1999 swfdec_action_type_of (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2001 SwfdecAsValue *val;
2002 const char *type;
2004 val = swfdec_as_stack_peek (cx, 1);
2005 switch (val->type) {
2006 case SWFDEC_AS_TYPE_NUMBER:
2007 type = SWFDEC_AS_STR_number;
2008 break;
2009 case SWFDEC_AS_TYPE_BOOLEAN:
2010 type = SWFDEC_AS_STR_boolean;
2011 break;
2012 case SWFDEC_AS_TYPE_STRING:
2013 type = SWFDEC_AS_STR_string;
2014 break;
2015 case SWFDEC_AS_TYPE_UNDEFINED:
2016 type = SWFDEC_AS_STR_undefined;
2017 break;
2018 case SWFDEC_AS_TYPE_NULL:
2019 type = SWFDEC_AS_STR_null;
2020 break;
2021 case SWFDEC_AS_TYPE_OBJECT:
2023 SwfdecAsObject *obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
2024 if (SWFDEC_IS_MOVIE (obj)) {
2025 type = SWFDEC_AS_STR_movieclip;
2026 } else if (SWFDEC_IS_AS_FUNCTION (obj)) {
2027 type = SWFDEC_AS_STR_function;
2028 } else {
2029 type = SWFDEC_AS_STR_object;
2032 break;
2033 default:
2034 g_assert_not_reached ();
2035 type = SWFDEC_AS_STR_EMPTY;
2036 break;
2038 SWFDEC_AS_VALUE_SET_STRING (val, type);
2041 static void
2042 swfdec_action_get_time (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2044 GTimeVal tv;
2045 gulong diff;
2047 swfdec_as_context_get_time (cx, &tv);
2048 /* we assume here that swfdec_as_context_get_time always returns a tv > start_time */
2049 diff = tv.tv_sec - cx->start_time.tv_sec;
2050 if (diff > G_MAXULONG / 1000 - 1) {
2051 SWFDEC_ERROR ("FIXME: time overflow");
2053 diff *= 1000;
2054 diff = diff + (tv.tv_usec - cx->start_time.tv_usec) / 1000;
2056 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_push (cx), diff);
2059 static void
2060 swfdec_action_extends (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2062 SwfdecAsValue *superclass, *subclass, proto;
2063 SwfdecAsObject *prototype;
2064 SwfdecAsObject *super;
2066 superclass = swfdec_as_stack_peek (cx, 1);
2067 subclass = swfdec_as_stack_peek (cx, 2);
2068 if (!SWFDEC_AS_VALUE_IS_OBJECT (superclass) ||
2069 !SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (superclass))) {
2070 SWFDEC_ERROR ("superclass is not a function");
2071 goto fail;
2073 if (!SWFDEC_AS_VALUE_IS_OBJECT (subclass)) {
2074 SWFDEC_ERROR ("subclass is not an object");
2075 goto fail;
2077 super = SWFDEC_AS_VALUE_GET_OBJECT (superclass);
2078 prototype = swfdec_as_object_new_empty (cx);
2079 if (prototype == NULL)
2080 return;
2081 swfdec_as_object_get_variable (super, SWFDEC_AS_STR_prototype, &proto);
2082 swfdec_as_object_set_variable (prototype, SWFDEC_AS_STR___proto__, &proto);
2083 if (cx->version > 5) {
2084 swfdec_as_object_set_variable_and_flags (prototype, SWFDEC_AS_STR___constructor__,
2085 superclass, SWFDEC_AS_VARIABLE_HIDDEN);
2087 SWFDEC_AS_VALUE_SET_OBJECT (&proto, prototype);
2088 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_OBJECT (subclass),
2089 SWFDEC_AS_STR_prototype, &proto);
2090 fail:
2091 swfdec_as_stack_pop_n (cx, 2);
2094 static gboolean
2095 swfdec_action_enumerate_foreach (SwfdecAsObject *object, const char *variable,
2096 SwfdecAsValue *value, guint flags, gpointer listp)
2098 GSList **list = listp;
2100 if (flags & SWFDEC_AS_VARIABLE_HIDDEN)
2101 return TRUE;
2103 *list = g_slist_remove (*list, variable);
2104 *list = g_slist_prepend (*list, (gpointer) variable);
2105 return TRUE;
2108 static void
2109 swfdec_action_do_enumerate (SwfdecAsContext *cx, SwfdecAsObject *object)
2111 guint i;
2112 GSList *walk, *list = NULL;
2114 for (i = 0; i < 256 && object; i++) {
2115 swfdec_as_object_foreach (object, swfdec_action_enumerate_foreach, &list);
2116 object = object->prototype;
2118 if (i == 256) {
2119 swfdec_as_context_abort (object->context, "Prototype recursion limit exceeded");
2120 g_slist_free (list);
2121 return;
2123 list = g_slist_reverse (list);
2124 i = 0;
2125 for (walk = list; walk; walk = walk->next) {
2126 /* 8 is an arbitrary value */
2127 if (i % 8 == 0) {
2128 swfdec_as_stack_ensure_free (cx, 8);
2129 i = 0;
2131 i++;
2132 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx), walk->data);
2134 g_slist_free (list);
2137 static void
2138 swfdec_action_enumerate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2140 SwfdecAsValue *val;
2141 SwfdecAsObject *obj;
2143 val = swfdec_as_stack_peek (cx, 1);
2145 swfdec_as_interpret_eval (cx, NULL, val);
2146 if (!SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2147 SWFDEC_ERROR ("Enumerate not pointing to an object");
2148 SWFDEC_AS_VALUE_SET_NULL (val);
2149 return;
2151 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
2152 SWFDEC_AS_VALUE_SET_NULL (val);
2153 swfdec_action_do_enumerate (cx, obj);
2156 static void
2157 swfdec_action_enumerate2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2159 SwfdecAsValue *val;
2160 SwfdecAsObject *obj;
2162 val = swfdec_as_stack_peek (cx, 1);
2163 if (!SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2164 SWFDEC_ERROR ("Enumerate2 called without an object");
2165 SWFDEC_AS_VALUE_SET_NULL (val);
2166 return;
2168 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
2169 SWFDEC_AS_VALUE_SET_NULL (val);
2170 swfdec_action_do_enumerate (cx, obj);
2173 static void
2174 swfdec_action_logical (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2176 SwfdecAsValue *val;
2177 gboolean l, r;
2179 l = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1));
2180 val = swfdec_as_stack_peek (cx, 2);
2181 r = swfdec_as_value_to_boolean (cx, val);
2183 SWFDEC_AS_VALUE_SET_BOOLEAN (val, (action == 0x10) ? (l && r) : (l || r));
2184 swfdec_as_stack_pop (cx);
2187 static void
2188 swfdec_action_char_to_ascii_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2190 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2191 const char *s = swfdec_as_value_to_string (cx, val);
2193 char *ascii;
2194 ascii = g_convert (s, -1, "LATIN1", "UTF-8", NULL, NULL, NULL);
2195 if (ascii == NULL) {
2196 /* This can happen if a Flash 5 movie gets loaded into a Flash 7 movie */
2197 SWFDEC_FIXME ("Someone threw unconvertible text %s at Flash <= 5", s);
2198 SWFDEC_AS_VALUE_SET_INT (val, 0); /* FIXME: what to return??? */
2199 } else {
2200 SWFDEC_AS_VALUE_SET_INT (val, (guchar) ascii[0]);
2201 g_free (ascii);
2205 static void
2206 swfdec_action_char_to_ascii (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2208 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2209 const char *s = swfdec_as_value_to_string(cx, val);
2210 gunichar *uni;
2212 uni = g_utf8_to_ucs4_fast (s, -1, NULL);
2213 if (uni == NULL) {
2214 /* This should never happen, everything is valid UTF-8 in here */
2215 g_warning ("conversion of character %s failed", s);
2216 SWFDEC_AS_VALUE_SET_INT (val, 0);
2217 } else {
2218 SWFDEC_AS_VALUE_SET_INT (val, uni[0]);
2219 g_free (uni);
2223 static void
2224 swfdec_action_ascii_to_char (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2226 char *s;
2227 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2228 gunichar c = ((guint) swfdec_as_value_to_integer (cx, val)) % 65536;
2230 s = g_ucs4_to_utf8 (&c, 1, NULL, NULL, NULL);
2231 if (s == NULL) {
2232 g_warning ("conversion of character %u failed", (guint) c);
2233 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2234 } else {
2235 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, s));
2236 g_free (s);
2240 static void
2241 swfdec_action_ascii_to_char_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2243 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2244 char s[2];
2245 char *utf8;
2247 s[0] = ((guint) swfdec_as_value_to_integer (cx, val)) % 256;
2248 s[1] = 0;
2250 utf8 = g_convert (s, -1, "UTF-8", "LATIN1", NULL, NULL, NULL);
2251 if (utf8 == NULL) {
2252 g_warning ("conversion of character %u failed", (guint) s[0]);
2253 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2254 } else {
2255 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, utf8));
2256 g_free (utf8);
2260 static void
2261 swfdec_action_mb_ascii_to_char_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2263 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2264 char s[3];
2265 char *utf8;
2266 guint i;
2268 i = ((guint) swfdec_as_value_to_integer (cx, val));
2269 if (i > 255) {
2270 s[0] = i / 256;
2271 s[1] = i % 256;
2272 s[2] = 0;
2273 } else {
2274 s[0] = i;
2275 s[1] = 0;
2277 utf8 = g_convert (s, -1, "UTF-8", "LATIN1", NULL, NULL, NULL);
2278 if (utf8 == NULL) {
2279 g_warning ("conversion of character %u failed", i);
2280 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2281 } else {
2282 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, utf8));
2283 g_free (utf8);
2287 static void
2288 swfdec_action_pop_with (SwfdecAsFrame *frame, gpointer with_object)
2290 g_assert (frame->scope_chain->data == with_object);
2291 frame->scope_chain = g_slist_delete_link (frame->scope_chain, frame->scope_chain);
2292 swfdec_as_frame_pop_block (frame);
2295 static void
2296 swfdec_action_with (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2298 SwfdecAsObject *object;
2299 guint offset;
2301 if (len != 2) {
2302 SWFDEC_ERROR ("With action requires a length of 2, but got %u", len);
2303 swfdec_as_stack_pop (cx);
2304 return;
2306 offset = data[0] | (data[1] << 8);
2307 object = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 1));
2308 if (object == NULL) {
2309 SWFDEC_INFO ("With called without an object, skipping");
2310 cx->frame->pc = (guint8 *) data + len + offset;
2311 } else {
2312 cx->frame->scope_chain = g_slist_prepend (cx->frame->scope_chain, object);
2313 swfdec_as_frame_push_block (cx->frame, data + len, data + len + offset,
2314 swfdec_action_pop_with, object, NULL);
2316 swfdec_as_stack_pop (cx);
2319 static void
2320 swfdec_action_remove_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2322 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2323 SwfdecAsObject *sprite;
2324 SwfdecMovie *movie;
2326 if (SWFDEC_AS_VALUE_IS_STRING (val)) {
2327 const char *name = SWFDEC_AS_VALUE_GET_STRING (val);
2329 swfdec_as_context_eval (cx, NULL, name, val);
2331 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2332 sprite = SWFDEC_AS_VALUE_GET_OBJECT (val);
2333 } else {
2334 SWFDEC_FIXME ("unknown type in RemoveSprite");
2335 goto fail;
2337 if (!SWFDEC_IS_MOVIE (sprite)) {
2338 SWFDEC_FIXME ("cannot remove non movieclip objects");
2339 goto fail;
2341 movie = SWFDEC_MOVIE (sprite);
2342 if (swfdec_depth_classify (movie->depth) == SWFDEC_DEPTH_CLASS_DYNAMIC) {
2343 SWFDEC_LOG ("removing clip %s", movie->name);
2344 swfdec_movie_remove (movie);
2346 fail:
2347 swfdec_as_stack_pop (cx);
2350 static void
2351 swfdec_action_clone_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2353 SwfdecMovie *movie, *new_movie;
2354 SwfdecAsObject *obj;
2355 const char *new_name;
2356 int depth;
2358 depth = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1)) - 16384;
2359 new_name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
2360 if (SWFDEC_AS_VALUE_IS_STRING (swfdec_as_stack_peek (cx, 3))) {
2361 const char *name;
2362 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 3));
2363 swfdec_as_context_eval (cx, NULL, name, swfdec_as_stack_peek (cx, 3));
2365 obj = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 3));
2366 if (!SWFDEC_IS_MOVIE(obj)) {
2367 SWFDEC_ERROR ("Object is not an SwfdecMovie object");
2368 swfdec_as_stack_pop_n (cx, 3);
2369 return;
2371 movie = SWFDEC_MOVIE(obj);
2372 new_movie = swfdec_movie_duplicate (movie, new_name, depth);
2373 if (new_movie) {
2374 SWFDEC_LOG ("duplicated %s as %s to depth %u", movie->name, new_movie->name, new_movie->depth);
2375 if (SWFDEC_IS_SPRITE_MOVIE (new_movie)) {
2376 g_queue_push_tail (SWFDEC_PLAYER (cx)->init_queue, new_movie);
2377 swfdec_movie_queue_script (new_movie, SWFDEC_EVENT_LOAD);
2378 swfdec_movie_run_construct (new_movie);
2380 swfdec_movie_initialize (new_movie);
2382 swfdec_as_stack_pop_n (cx, 3);
2385 /*** PRINT FUNCTIONS ***/
2387 static char *
2388 swfdec_action_print_with (guint action, const guint8 *data, guint len)
2390 if (len != 2) {
2391 SWFDEC_ERROR ("With action requires a length of 2, but got %u", len);
2392 return NULL;
2394 return g_strdup_printf ("With %u", data[0] | (data[1] << 8));
2397 static char *
2398 swfdec_action_print_store_register (guint action, const guint8 *data, guint len)
2400 if (len != 1) {
2401 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
2402 return NULL;
2404 return g_strdup_printf ("StoreRegister %u", (guint) *data);
2407 static char *
2408 swfdec_action_print_set_target (guint action, const guint8 *data, guint len)
2410 if (!memchr (data, 0, len)) {
2411 SWFDEC_ERROR ("SetTarget action does not specify a string");
2412 return NULL;
2414 return g_strconcat ("SetTarget ", data, NULL);
2417 static char *
2418 swfdec_action_print_define_function (guint action, const guint8 *data, guint len)
2420 SwfdecBits bits;
2421 GString *string;
2422 const char *function_name;
2423 guint i, n_args, size;
2424 gboolean v2 = (action == 0x8e);
2426 string = g_string_new (v2 ? "DefineFunction2 " : "DefineFunction ");
2427 swfdec_bits_init_data (&bits, data, len);
2428 function_name = swfdec_bits_get_string (&bits);
2429 if (function_name == NULL) {
2430 SWFDEC_ERROR ("could not parse function name");
2431 g_string_free (string, TRUE);
2432 return NULL;
2434 if (*function_name) {
2435 g_string_append (string, function_name);
2436 g_string_append_c (string, ' ');
2438 n_args = swfdec_bits_get_u16 (&bits);
2439 g_string_append_c (string, '(');
2440 if (v2) {
2441 /* n_regs = */ swfdec_bits_get_u8 (&bits);
2442 /* flags = */ swfdec_bits_get_u16 (&bits);
2445 for (i = 0; i < n_args; i++) {
2446 guint preload;
2447 const char *arg_name;
2448 if (v2)
2449 preload = swfdec_bits_get_u8 (&bits);
2450 else
2451 preload = 0;
2452 arg_name = swfdec_bits_get_string (&bits);
2453 if (preload == 0 && (arg_name == NULL || *arg_name == '\0')) {
2454 SWFDEC_ERROR ("empty argument name not allowed");
2455 g_string_free (string, TRUE);
2456 return NULL;
2458 if (i)
2459 g_string_append (string, ", ");
2460 g_string_append (string, arg_name);
2461 if (preload)
2462 g_string_append_printf (string, " (%u)", preload);
2464 g_string_append_c (string, ')');
2465 size = swfdec_bits_get_u16 (&bits);
2466 g_string_append_printf (string, " %u", size);
2467 return g_string_free (string, FALSE);
2470 static char *
2471 swfdec_action_print_get_url2 (guint action, const guint8 *data, guint len)
2473 guint method;
2475 if (len != 1) {
2476 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
2477 return NULL;
2479 method = data[0] >> 6;
2480 if (method == 3) {
2481 SWFDEC_ERROR ("GetURL method 3 invalid");
2482 method = 0;
2484 if (method) {
2485 SWFDEC_FIXME ("implement encoding variables using %s", method == 1 ? "GET" : "POST");
2487 return g_strdup_printf ("GetURL2%s%s%s", method == 0 ? "" : (method == 1 ? " GET" : " POST"),
2488 data[0] & 2 ? " LoadTarget" : "", data[0] & 1 ? " LoadVariables" : "");
2491 static char *
2492 swfdec_action_print_get_url (guint action, const guint8 *data, guint len)
2494 SwfdecBits bits;
2495 char *url, *target, *ret;
2497 swfdec_bits_init_data (&bits, data, len);
2498 url = swfdec_bits_get_string (&bits);
2499 target = swfdec_bits_get_string (&bits);
2500 if (url == NULL) {
2501 SWFDEC_ERROR ("not enough data in GetURL");
2502 url = g_strdup ("???");
2504 if (target == NULL) {
2505 SWFDEC_ERROR ("not enough data in GetURL");
2506 target = g_strdup ("???");
2508 if (swfdec_bits_left (&bits)) {
2509 SWFDEC_WARNING ("leftover bytes in GetURL action");
2511 ret = g_strdup_printf ("GetURL %s %s", url, target);
2512 g_free (url);
2513 g_free (target);
2514 return ret;
2517 static char *
2518 swfdec_action_print_if (guint action, const guint8 *data, guint len)
2520 if (len != 2) {
2521 SWFDEC_ERROR ("If action length invalid (is %u, should be 2", len);
2522 return NULL;
2524 return g_strdup_printf ("If %d", GINT16_FROM_LE (*((gint16*) data)));
2527 static char *
2528 swfdec_action_print_jump (guint action, const guint8 *data, guint len)
2530 if (len != 2) {
2531 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
2532 return NULL;
2534 return g_strdup_printf ("Jump %d", GINT16_FROM_LE (*((gint16*) data)));
2537 static char *
2538 swfdec_action_print_push (guint action, const guint8 *data, guint len)
2540 gboolean first = TRUE;
2541 SwfdecBits bits;
2542 GString *string = g_string_new ("Push");
2544 swfdec_bits_init_data (&bits, data, len);
2545 while (swfdec_bits_left (&bits)) {
2546 guint type = swfdec_bits_get_u8 (&bits);
2547 if (first)
2548 g_string_append (string, " ");
2549 else
2550 g_string_append (string, ", ");
2551 first = FALSE;
2552 switch (type) {
2553 case 0: /* string */
2555 /* FIXME: need version! */
2556 char *s = swfdec_bits_get_string (&bits);
2557 if (!s) {
2558 g_string_free (string, TRUE);
2559 return NULL;
2561 g_string_append_c (string, '"');
2562 g_string_append (string, s);
2563 g_string_append_c (string, '"');
2564 g_free (s);
2565 break;
2567 case 1: /* float */
2568 g_string_append_printf (string, "%g", swfdec_bits_get_float (&bits));
2569 break;
2570 case 2: /* null */
2571 g_string_append (string, "null");
2572 break;
2573 case 3: /* undefined */
2574 g_string_append (string, "void");
2575 break;
2576 case 4: /* register */
2577 g_string_append_printf (string, "Register %u", swfdec_bits_get_u8 (&bits));
2578 break;
2579 case 5: /* boolean */
2580 g_string_append (string, swfdec_bits_get_u8 (&bits) ? "True" : "False");
2581 break;
2582 case 6: /* double */
2583 g_string_append_printf (string, "%g", swfdec_bits_get_double (&bits));
2584 break;
2585 case 7: /* 32bit int */
2586 g_string_append_printf (string, "%d", swfdec_bits_get_u32 (&bits));
2587 break;
2588 case 8: /* 8bit ConstantPool address */
2589 g_string_append_printf (string, "Pool %u", swfdec_bits_get_u8 (&bits));
2590 break;
2591 case 9: /* 16bit ConstantPool address */
2592 g_string_append_printf (string, "Pool %u", swfdec_bits_get_u16 (&bits));
2593 break;
2594 default:
2595 SWFDEC_ERROR ("Push: type %u not implemented", type);
2596 return NULL;
2599 return g_string_free (string, FALSE);
2602 /* NB: constant pool actions are special in that they are called at init time */
2603 static char *
2604 swfdec_action_print_constant_pool (guint action, const guint8 *data, guint len)
2606 guint i;
2607 GString *string;
2608 SwfdecConstantPool *pool;
2610 /* FIXME: version */
2611 pool = swfdec_constant_pool_new_from_action (data, len, 6);
2612 if (pool == NULL)
2613 return g_strdup ("ConstantPool (invalid)");
2614 string = g_string_new ("ConstantPool");
2615 for (i = 0; i < swfdec_constant_pool_size (pool); i++) {
2616 g_string_append (string, i ? ", " : " ");
2617 g_string_append (string, swfdec_constant_pool_get (pool, i));
2618 g_string_append_printf (string, " (%u)", i);
2620 return g_string_free (string, FALSE);
2623 #if 0
2624 static char *
2625 swfdec_action_print_wait_for_frame2 (guint action, const guint8 *data, guint len)
2627 if (len != 1) {
2628 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
2629 return NULL;
2631 return g_strdup_printf ("WaitForFrame2 %u", (guint) *data);
2633 #endif
2635 static char *
2636 swfdec_action_print_goto_frame2 (guint action, const guint8 *data, guint len)
2638 gboolean play, bias;
2639 SwfdecBits bits;
2641 swfdec_bits_init_data (&bits, data, len);
2642 if (swfdec_bits_getbits (&bits, 6)) {
2643 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
2645 bias = swfdec_bits_getbit (&bits);
2646 play = swfdec_bits_getbit (&bits);
2647 if (bias) {
2648 return g_strdup_printf ("GotoFrame2 %s +%u", play ? "play" : "stop",
2649 swfdec_bits_get_u16 (&bits));
2650 } else {
2651 return g_strdup_printf ("GotoFrame2 %s", play ? "play" : "stop");
2655 static char *
2656 swfdec_action_print_goto_frame (guint action, const guint8 *data, guint len)
2658 guint frame;
2660 if (len != 2)
2661 return NULL;
2663 frame = data[0] | (data[1] << 8);
2664 return g_strdup_printf ("GotoFrame %u", frame);
2667 static char *
2668 swfdec_action_print_goto_label (guint action, const guint8 *data, guint len)
2670 if (!memchr (data, 0, len)) {
2671 SWFDEC_ERROR ("GotoLabel action does not specify a string");
2672 return NULL;
2675 return g_strdup_printf ("GotoLabel %s", data);
2678 static char *
2679 swfdec_action_print_wait_for_frame (guint action, const guint8 *data, guint len)
2681 guint frame, jump;
2683 if (len != 3)
2684 return NULL;
2686 frame = data[0] | (data[1] << 8);
2687 jump = data[2];
2688 return g_strdup_printf ("WaitForFrame %u %u", frame, jump);
2691 /*** BIG FUNCTION TABLE ***/
2693 const SwfdecActionSpec swfdec_as_actions[256] = {
2694 /* version 3 */
2695 [SWFDEC_AS_ACTION_NEXT_FRAME] = { "NextFrame", NULL, 0, 0, { swfdec_action_next_frame, swfdec_action_next_frame, swfdec_action_next_frame, swfdec_action_next_frame, swfdec_action_next_frame } },
2696 [SWFDEC_AS_ACTION_PREVIOUS_FRAME] = { "PreviousFrame", NULL, 0, 0, { swfdec_action_previous_frame, swfdec_action_previous_frame, swfdec_action_previous_frame, swfdec_action_previous_frame, swfdec_action_previous_frame } },
2697 [SWFDEC_AS_ACTION_PLAY] = { "Play", NULL, 0, 0, { swfdec_action_play, swfdec_action_play, swfdec_action_play, swfdec_action_play, swfdec_action_play } },
2698 [SWFDEC_AS_ACTION_STOP] = { "Stop", NULL, 0, 0, { swfdec_action_stop, swfdec_action_stop, swfdec_action_stop, swfdec_action_stop, swfdec_action_stop } },
2699 [SWFDEC_AS_ACTION_TOGGLE_QUALITY] = { "ToggleQuality", NULL },
2700 [SWFDEC_AS_ACTION_STOP_SOUNDS] = { "StopSounds", NULL, 0, 0, { swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds } },
2701 /* version 4 */
2702 [SWFDEC_AS_ACTION_ADD] = { "Add", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } },
2703 [SWFDEC_AS_ACTION_SUBTRACT] = { "Subtract", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } },
2704 [SWFDEC_AS_ACTION_MULTIPLY] = { "Multiply", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } },
2705 [SWFDEC_AS_ACTION_DIVIDE] = { "Divide", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } },
2706 [SWFDEC_AS_ACTION_EQUALS] = { "Equals", NULL, 2, 1, { NULL, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare } },
2707 [SWFDEC_AS_ACTION_LESS] = { "Less", NULL, 2, 1, { NULL, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare } },
2708 [SWFDEC_AS_ACTION_AND] = { "And", NULL, 2, 1, { NULL, /* FIXME */NULL, swfdec_action_logical, swfdec_action_logical, swfdec_action_logical } },
2709 [SWFDEC_AS_ACTION_OR] = { "Or", NULL, 2, 1, { NULL, /* FIXME */NULL, swfdec_action_logical, swfdec_action_logical, swfdec_action_logical } },
2710 [SWFDEC_AS_ACTION_NOT] = { "Not", NULL, 1, 1, { NULL, swfdec_action_not_4, swfdec_action_not_5, swfdec_action_not_5, swfdec_action_not_5 } },
2711 [SWFDEC_AS_ACTION_STRING_EQUALS] = { "StringEquals", NULL, 2, 1, { NULL, swfdec_action_string_compare, swfdec_action_string_compare, swfdec_action_string_compare, swfdec_action_string_compare } },
2712 [SWFDEC_AS_ACTION_STRING_LENGTH] = { "StringLength", NULL, 1, 1, { NULL, swfdec_action_string_length, swfdec_action_string_length, swfdec_action_string_length, swfdec_action_string_length } },
2713 [SWFDEC_AS_ACTION_STRING_EXTRACT] = { "StringExtract", NULL },
2714 [SWFDEC_AS_ACTION_POP] = { "Pop", NULL, 1, 0, { NULL, swfdec_action_pop, swfdec_action_pop, swfdec_action_pop, swfdec_action_pop } },
2715 [SWFDEC_AS_ACTION_TO_INTEGER] = { "ToInteger", NULL, 1, 1, { NULL, swfdec_action_to_integer, swfdec_action_to_integer, swfdec_action_to_integer, swfdec_action_to_integer } },
2716 [SWFDEC_AS_ACTION_GET_VARIABLE] = { "GetVariable", NULL, 1, 1, { NULL, swfdec_action_get_variable, swfdec_action_get_variable, swfdec_action_get_variable, swfdec_action_get_variable } },
2717 [SWFDEC_AS_ACTION_SET_VARIABLE] = { "SetVariable", NULL, 2, 0, { NULL, swfdec_action_set_variable, swfdec_action_set_variable, swfdec_action_set_variable, swfdec_action_set_variable } },
2718 [SWFDEC_AS_ACTION_SET_TARGET2] = { "SetTarget2", NULL, 1, 0, { swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2 } },
2719 [0x21] = { "StringAdd", NULL, 2, 1, { NULL, swfdec_action_string_add, swfdec_action_string_add, swfdec_action_string_add, swfdec_action_string_add } },
2720 [SWFDEC_AS_ACTION_GET_PROPERTY] = { "GetProperty", NULL, 2, 1, { NULL, swfdec_action_get_property, swfdec_action_get_property, swfdec_action_get_property, swfdec_action_get_property } },
2721 [SWFDEC_AS_ACTION_SET_PROPERTY] = { "SetProperty", NULL, 3, 0, { NULL, swfdec_action_set_property, swfdec_action_set_property, swfdec_action_set_property, swfdec_action_set_property } },
2722 [SWFDEC_AS_ACTION_CLONE_SPRITE] = { "CloneSprite", NULL, 3, 0, { NULL, swfdec_action_clone_sprite, swfdec_action_clone_sprite, swfdec_action_clone_sprite, swfdec_action_clone_sprite } },
2723 [SWFDEC_AS_ACTION_REMOVE_SPRITE] = { "RemoveSprite", NULL, 1, 0, { NULL, swfdec_action_remove_sprite, swfdec_action_remove_sprite, swfdec_action_remove_sprite, swfdec_action_remove_sprite } },
2724 [SWFDEC_AS_ACTION_TRACE] = { "Trace", NULL, 1, 0, { NULL, swfdec_action_trace, swfdec_action_trace, swfdec_action_trace, swfdec_action_trace } },
2725 [SWFDEC_AS_ACTION_START_DRAG] = { "StartDrag", NULL, -1, 0, { NULL, swfdec_action_start_drag, swfdec_action_start_drag, swfdec_action_start_drag, swfdec_action_start_drag } },
2726 [SWFDEC_AS_ACTION_END_DRAG] = { "EndDrag", NULL, 0, 0, { NULL, swfdec_action_end_drag, swfdec_action_end_drag, swfdec_action_end_drag, swfdec_action_end_drag } },
2727 [SWFDEC_AS_ACTION_STRING_LESS] = { "StringLess", NULL, 2, 1, { NULL, swfdec_action_string_compare, swfdec_action_string_compare, swfdec_action_string_compare, swfdec_action_string_compare } },
2728 /* version 7 */
2729 [SWFDEC_AS_ACTION_THROW] = { "Throw", NULL },
2730 [SWFDEC_AS_ACTION_CAST] = { "Cast", NULL },
2731 [SWFDEC_AS_ACTION_IMPLEMENTS] = { "Implements", NULL },
2732 /* version 4 */
2733 [0x30] = { "RandomNumber", NULL, 1, 1, { NULL, swfdec_action_random_number, swfdec_action_random_number, swfdec_action_random_number, swfdec_action_random_number } },
2734 [SWFDEC_AS_ACTION_MB_STRING_LENGTH] = { "MBStringLength", NULL },
2735 [SWFDEC_AS_ACTION_CHAR_TO_ASCII] = { "CharToAscii", NULL, 1, 1, { NULL, swfdec_action_char_to_ascii_5, swfdec_action_char_to_ascii_5, swfdec_action_char_to_ascii, swfdec_action_char_to_ascii } },
2736 [SWFDEC_AS_ACTION_ASCII_TO_CHAR] = { "AsciiToChar", NULL, 1, 1, { NULL, swfdec_action_ascii_to_char_5, swfdec_action_ascii_to_char_5, swfdec_action_ascii_to_char, swfdec_action_ascii_to_char } },
2737 [SWFDEC_AS_ACTION_GET_TIME] = { "GetTime", NULL, 0, 1, { NULL, swfdec_action_get_time, swfdec_action_get_time, swfdec_action_get_time, swfdec_action_get_time } },
2738 [SWFDEC_AS_ACTION_MB_STRING_EXTRACT] = { "MBStringExtract", NULL },
2739 [SWFDEC_AS_ACTION_MB_CHAR_TO_ASCII] = { "MBCharToAscii", NULL },
2740 [SWFDEC_AS_ACTION_MB_ASCII_TO_CHAR] = { "MBAsciiToChar", NULL, 1, 1, { NULL, swfdec_action_mb_ascii_to_char_5, swfdec_action_mb_ascii_to_char_5, swfdec_action_ascii_to_char, swfdec_action_ascii_to_char } },
2741 /* version 5 */
2742 [SWFDEC_AS_ACTION_DELETE] = { "Delete", NULL, 2, 1, { NULL, NULL, swfdec_action_delete, swfdec_action_delete, swfdec_action_delete } },
2743 [SWFDEC_AS_ACTION_DELETE2] = { "Delete2", NULL, 1, 1, { NULL, NULL, swfdec_action_delete2, swfdec_action_delete2, swfdec_action_delete2 } },
2744 [SWFDEC_AS_ACTION_DEFINE_LOCAL] = { "DefineLocal", NULL, 2, 0, { NULL, NULL, swfdec_action_define_local, swfdec_action_define_local, swfdec_action_define_local } },
2745 [SWFDEC_AS_ACTION_CALL_FUNCTION] = { "CallFunction", NULL, -1, 1, { NULL, NULL, swfdec_action_call_function, swfdec_action_call_function, swfdec_action_call_function } },
2746 [SWFDEC_AS_ACTION_RETURN] = { "Return", NULL, 1, 0, { NULL, NULL, swfdec_action_return, swfdec_action_return, swfdec_action_return } },
2747 [SWFDEC_AS_ACTION_MODULO] = { "Modulo", NULL, 2, 1, { NULL, NULL, swfdec_action_modulo, swfdec_action_modulo, swfdec_action_modulo } },
2748 [SWFDEC_AS_ACTION_NEW_OBJECT] = { "NewObject", NULL, -1, 1, { NULL, NULL, swfdec_action_new_object, swfdec_action_new_object, swfdec_action_new_object } },
2749 [SWFDEC_AS_ACTION_DEFINE_LOCAL2] = { "DefineLocal2", NULL, 1, 0, { NULL, NULL, swfdec_action_define_local2, swfdec_action_define_local2, swfdec_action_define_local2 } },
2750 [SWFDEC_AS_ACTION_INIT_ARRAY] = { "InitArray", NULL, -1, 1, { NULL, NULL, swfdec_action_init_array, swfdec_action_init_array, swfdec_action_init_array } },
2751 [SWFDEC_AS_ACTION_INIT_OBJECT] = { "InitObject", NULL, -1, 1, { NULL, NULL, swfdec_action_init_object, swfdec_action_init_object, swfdec_action_init_object } },
2752 [SWFDEC_AS_ACTION_TYPE_OF] = { "TypeOf", NULL, 1, 1, { NULL, NULL, swfdec_action_type_of, swfdec_action_type_of, swfdec_action_type_of } },
2753 [SWFDEC_AS_ACTION_TARGET_PATH] = { "TargetPath", NULL, 1, 1, { NULL, NULL, swfdec_action_target_path, swfdec_action_target_path, swfdec_action_target_path } },
2754 [SWFDEC_AS_ACTION_ENUMERATE] = { "Enumerate", NULL, 1, -1, { NULL, NULL, swfdec_action_enumerate, swfdec_action_enumerate, swfdec_action_enumerate } },
2755 [SWFDEC_AS_ACTION_ADD2] = { "Add2", NULL, 2, 1, { NULL, NULL, swfdec_action_add2, swfdec_action_add2, swfdec_action_add2 } },
2756 [SWFDEC_AS_ACTION_LESS2] = { "Less2", NULL, 2, 1, { NULL, NULL, swfdec_action_new_comparison, swfdec_action_new_comparison, swfdec_action_new_comparison } },
2757 [SWFDEC_AS_ACTION_EQUALS2] = { "Equals2", NULL, 2, 1, { NULL, NULL, swfdec_action_equals2_5, swfdec_action_equals2, swfdec_action_equals2 } },
2758 [SWFDEC_AS_ACTION_TO_NUMBER] = { "ToNumber", NULL, 1, 1, { NULL, NULL, swfdec_action_to_number, swfdec_action_to_number, swfdec_action_to_number } },
2759 [SWFDEC_AS_ACTION_TO_STRING] = { "ToString", NULL, 1, 1, { NULL, NULL, swfdec_action_to_string, swfdec_action_to_string, swfdec_action_to_string } },
2760 [SWFDEC_AS_ACTION_PUSH_DUPLICATE] = { "PushDuplicate", NULL, 1, 2, { NULL, NULL, swfdec_action_push_duplicate, swfdec_action_push_duplicate, swfdec_action_push_duplicate } },
2761 [SWFDEC_AS_ACTION_SWAP] = { "Swap", NULL, 2, 2, { NULL, NULL, swfdec_action_swap, swfdec_action_swap, swfdec_action_swap } },
2762 [SWFDEC_AS_ACTION_GET_MEMBER] = { "GetMember", NULL, 2, 1, { NULL, swfdec_action_get_member, swfdec_action_get_member, swfdec_action_get_member, swfdec_action_get_member } },
2763 [SWFDEC_AS_ACTION_SET_MEMBER] = { "SetMember", NULL, 3, 0, { NULL, swfdec_action_set_member, swfdec_action_set_member, swfdec_action_set_member, swfdec_action_set_member } },
2764 [SWFDEC_AS_ACTION_INCREMENT] = { "Increment", NULL, 1, 1, { NULL, NULL, swfdec_action_increment, swfdec_action_increment, swfdec_action_increment } },
2765 [SWFDEC_AS_ACTION_DECREMENT] = { "Decrement", NULL, 1, 1, { NULL, NULL, swfdec_action_decrement, swfdec_action_decrement, swfdec_action_decrement } },
2766 [SWFDEC_AS_ACTION_CALL_METHOD] = { "CallMethod", NULL, -1, 1, { NULL, NULL, swfdec_action_call_method, swfdec_action_call_method, swfdec_action_call_method } },
2767 [SWFDEC_AS_ACTION_NEW_METHOD] = { "NewMethod", NULL, -1, 1, { NULL, NULL, swfdec_action_new_method, swfdec_action_new_method, swfdec_action_new_method } },
2768 /* version 6 */
2769 [SWFDEC_AS_ACTION_INSTANCE_OF] = { "InstanceOf", NULL },
2770 [SWFDEC_AS_ACTION_ENUMERATE2] = { "Enumerate2", NULL, 1, -1, { NULL, NULL, NULL, swfdec_action_enumerate2, swfdec_action_enumerate2 } },
2771 /* version 5 */
2772 [SWFDEC_AS_ACTION_BIT_AND] = { "BitAnd", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } },
2773 [SWFDEC_AS_ACTION_BIT_OR] = { "BitOr", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } },
2774 [SWFDEC_AS_ACTION_BIT_XOR] = { "BitXor", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } },
2775 [SWFDEC_AS_ACTION_BIT_LSHIFT] = { "BitLShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } },
2776 [SWFDEC_AS_ACTION_BIT_RSHIFT] = { "BitRShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } },
2777 [SWFDEC_AS_ACTION_BIT_URSHIFT] = { "BitURShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } },
2778 /* version 6 */
2779 [SWFDEC_AS_ACTION_STRICT_EQUALS] = { "StrictEquals", NULL, 2, 1, { NULL, NULL, NULL, swfdec_action_strict_equals, swfdec_action_strict_equals } },
2780 [SWFDEC_AS_ACTION_GREATER] = { "Greater", NULL, 2, 1, { NULL, NULL, NULL, swfdec_action_new_comparison, swfdec_action_new_comparison } },
2781 [SWFDEC_AS_ACTION_STRING_GREATER] = { "StringGreater", NULL },
2782 /* version 7 */
2783 [SWFDEC_AS_ACTION_EXTENDS] = { "Extends", NULL, 2, 0, { NULL, NULL, NULL, swfdec_action_extends, swfdec_action_extends } },
2784 /* version 3 */
2785 [SWFDEC_AS_ACTION_GOTO_FRAME] = { "GotoFrame", swfdec_action_print_goto_frame, 0, 0, { swfdec_action_goto_frame, swfdec_action_goto_frame, swfdec_action_goto_frame, swfdec_action_goto_frame, swfdec_action_goto_frame } },
2786 [SWFDEC_AS_ACTION_GET_URL] = { "GetURL", swfdec_action_print_get_url, 0, 0, { swfdec_action_get_url, swfdec_action_get_url, swfdec_action_get_url, swfdec_action_get_url, swfdec_action_get_url } },
2787 /* version 5 */
2788 [SWFDEC_AS_ACTION_STORE_REGISTER] = { "StoreRegister", swfdec_action_print_store_register, 1, 1, { NULL, NULL, swfdec_action_store_register, swfdec_action_store_register, swfdec_action_store_register } },
2789 [SWFDEC_AS_ACTION_CONSTANT_POOL] = { "ConstantPool", swfdec_action_print_constant_pool, 0, 0, { NULL, NULL, swfdec_action_constant_pool, swfdec_action_constant_pool, swfdec_action_constant_pool } },
2790 /* version 3 */
2791 [SWFDEC_AS_ACTION_WAIT_FOR_FRAME] = { "WaitForFrame", swfdec_action_print_wait_for_frame, 0, 0, { swfdec_action_wait_for_frame, swfdec_action_wait_for_frame, swfdec_action_wait_for_frame, swfdec_action_wait_for_frame, swfdec_action_wait_for_frame } },
2792 [SWFDEC_AS_ACTION_SET_TARGET] = { "SetTarget", swfdec_action_print_set_target, 0, 0, { swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target } },
2793 [SWFDEC_AS_ACTION_GOTO_LABEL] = { "GotoLabel", swfdec_action_print_goto_label, 0, 0, { swfdec_action_goto_label, swfdec_action_goto_label, swfdec_action_goto_label, swfdec_action_goto_label, swfdec_action_goto_label } },
2794 #if 0
2795 /* version 4 */
2796 [0x8d] = { "WaitForFrame2", swfdec_action_print_wait_for_frame2, 1, 0, { NULL, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2, swfdec_action_wait_for_frame2 } },
2797 #endif
2798 /* version 7 */
2799 [SWFDEC_AS_ACTION_DEFINE_FUNCTION2] = { "DefineFunction2", swfdec_action_print_define_function, 0, -1, { NULL, NULL, NULL, swfdec_action_define_function, swfdec_action_define_function } },
2800 [SWFDEC_AS_ACTION_TRY] = { "Try", NULL },
2801 /* version 5 */
2802 [SWFDEC_AS_ACTION_WITH] = { "With", swfdec_action_print_with, 1, 0, { NULL, NULL, swfdec_action_with, swfdec_action_with, swfdec_action_with } },
2803 /* version 4 */
2804 [SWFDEC_AS_ACTION_PUSH] = { "Push", swfdec_action_print_push, 0, -1, { NULL, swfdec_action_push, swfdec_action_push, swfdec_action_push, swfdec_action_push } },
2805 [SWFDEC_AS_ACTION_JUMP] = { "Jump", swfdec_action_print_jump, 0, 0, { NULL, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump } },
2806 [SWFDEC_AS_ACTION_GET_URL2] = { "GetURL2", swfdec_action_print_get_url2, 2, 0, { NULL, swfdec_action_get_url2, swfdec_action_get_url2, swfdec_action_get_url2, swfdec_action_get_url2 } },
2807 /* version 5 */
2808 [SWFDEC_AS_ACTION_DEFINE_FUNCTION] = { "DefineFunction", swfdec_action_print_define_function, 0, -1, { NULL, NULL, swfdec_action_define_function, swfdec_action_define_function, swfdec_action_define_function } },
2809 /* version 4 */
2810 [SWFDEC_AS_ACTION_IF] = { "If", swfdec_action_print_if, 1, 0, { NULL, swfdec_action_if, swfdec_action_if, swfdec_action_if, swfdec_action_if } },
2811 [SWFDEC_AS_ACTION_CALL] = { "Call", NULL },
2812 [SWFDEC_AS_ACTION_GOTO_FRAME2] = { "GotoFrame2", swfdec_action_print_goto_frame2, 1, 0, { NULL, swfdec_action_goto_frame2, swfdec_action_goto_frame2, swfdec_action_goto_frame2, swfdec_action_goto_frame2 } }