fix jsut committed crasher by rewriting startDrag action
[swfdec.git] / libswfdec / swfdec_as_interpret.c
blobe8f6e644611db939f225d7bc2c046a44d4c2ee89
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_as_with.h"
34 #include "swfdec_debug.h"
36 #include <errno.h>
37 #include <math.h>
38 #include <string.h>
39 #include "swfdec_decoder.h"
40 #include "swfdec_movie.h"
41 #include "swfdec_player_internal.h"
42 #include "swfdec_sprite.h"
43 #include "swfdec_sprite_movie.h"
44 #include "swfdec_swf_instance.h"
46 /* Define this to get SWFDEC_WARN'd about missing properties of objects.
47 * This can be useful to find out about unimplemented native properties,
48 * but usually just causes a lot of spam. */
49 //#define SWFDEC_WARN_MISSING_PROPERTIES
51 /*** SUPPORT FUNCTIONS ***/
53 #define swfdec_action_has_register(cx, i) \
54 ((i) < (cx)->frame->n_registers)
56 /*** ALL THE ACTION IS HERE ***/
58 static void
59 swfdec_action_stop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
61 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
62 SWFDEC_SPRITE_MOVIE (cx->frame->target)->playing = FALSE;
63 else
64 SWFDEC_ERROR ("no movie to stop");
67 static void
68 swfdec_action_play (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
70 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
71 SWFDEC_SPRITE_MOVIE (cx->frame->target)->playing = TRUE;
72 else
73 SWFDEC_ERROR ("no movie to play");
76 static void
77 swfdec_action_next_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
79 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
80 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
81 if (movie->frame < movie->n_frames) {
82 swfdec_sprite_movie_goto (movie, movie->frame + 1);
83 } else {
84 SWFDEC_INFO ("can't execute nextFrame, already at last frame");
86 } else {
87 SWFDEC_ERROR ("no movie to nextFrame on");
91 static void
92 swfdec_action_previous_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
94 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
95 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
96 if (movie->frame > 1) {
97 swfdec_sprite_movie_goto (movie, movie->frame - 1);
98 } else {
99 SWFDEC_INFO ("can't execute previousFrame, already at first frame");
101 } else {
102 SWFDEC_ERROR ("no movie to previousFrame on");
106 static void
107 swfdec_action_goto_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
109 guint frame;
111 if (len != 2) {
112 SWFDEC_ERROR ("GotoFrame action length invalid (is %u, should be 2", len);
113 return;
115 frame = GUINT16_FROM_LE (*((guint16 *) data));
116 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
117 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
118 swfdec_sprite_movie_goto (movie, frame + 1);
119 movie->playing = FALSE;
120 } else {
121 SWFDEC_ERROR ("no movie to goto on");
125 static void
126 swfdec_action_goto_label (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
128 if (!memchr (data, 0, len)) {
129 SWFDEC_ERROR ("GotoLabel action does not specify a string");
130 return;
133 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
134 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
135 int frame;
136 if (movie->sprite == NULL ||
137 (frame = swfdec_sprite_get_frame (movie->sprite, (const char *) data)) == -1)
138 return;
139 swfdec_sprite_movie_goto (movie, frame + 1);
140 movie->playing = FALSE;
141 } else {
142 SWFDEC_ERROR ("no movie to goto on");
146 /* returns: frame to go to or 0 on error */
147 static guint
148 swfdec_value_to_frame (SwfdecAsContext *cx, SwfdecSpriteMovie *movie, SwfdecAsValue *val)
150 int frame;
152 if (movie->sprite == NULL)
153 return 0;
154 if (SWFDEC_AS_VALUE_IS_STRING (val)) {
155 const char *name = SWFDEC_AS_VALUE_GET_STRING (val);
156 double d;
157 if (strchr (name, ':')) {
158 SWFDEC_ERROR ("FIXME: handle targets");
160 /* treat valid encoded numbers as numbers, otherwise assume it's a frame label */
161 d = swfdec_as_value_to_number (cx, val);
162 if (isnan (d))
163 frame = swfdec_sprite_get_frame (movie->sprite, name) + 1;
164 else
165 frame = d;
166 } else if (SWFDEC_AS_VALUE_IS_NUMBER (val)) {
167 frame = swfdec_as_value_to_integer (cx, val);
168 } else {
169 SWFDEC_WARNING ("cannot convert value to frame number");
170 /* FIXME: how do we treat undefined etc? */
171 frame = 0;
173 return frame <= 0 ? 0 : frame;
176 static void
177 swfdec_action_goto_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
179 SwfdecBits bits;
180 guint bias;
181 gboolean play;
182 SwfdecAsValue *val;
184 swfdec_bits_init_data (&bits, data, len);
185 if (swfdec_bits_getbits (&bits, 6)) {
186 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
188 bias = swfdec_bits_getbit (&bits);
189 play = swfdec_bits_getbit (&bits);
190 if (bias) {
191 bias = swfdec_bits_get_u16 (&bits);
193 val = swfdec_as_stack_peek (cx, 1);
194 /* now set it */
195 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
196 SwfdecSpriteMovie *movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
197 guint frame = swfdec_value_to_frame (cx, movie, val);
198 if (frame > 0) {
199 frame += bias;
200 frame = CLAMP (frame, 1, movie->n_frames);
201 swfdec_sprite_movie_goto (movie, frame);
202 movie->playing = play;
204 } else {
205 SWFDEC_ERROR ("no movie to GotoFrame2 on");
207 swfdec_as_stack_pop (cx);
210 static void
211 swfdec_script_skip_actions (SwfdecAsContext *cx, guint jump)
213 SwfdecScript *script = cx->frame->script;
214 guint8 *pc = cx->frame->pc;
215 guint8 *endpc = script->buffer->data + script->buffer->length;
217 /* jump instructions */
218 do {
219 if (pc >= endpc)
220 break;
221 if (*pc & 0x80) {
222 if (pc + 2 >= endpc)
223 break;
224 pc += 3 + GUINT16_FROM_LE (*((guint16 *) (pc + 1)));
225 } else {
226 pc++;
228 } while (jump-- > 0);
229 cx->frame->pc = pc;
232 #if 0
233 static void
234 swfdec_action_wait_for_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
236 jsval val;
238 if (len != 1) {
239 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
240 return JS_FALSE;
242 val = cx->fp->sp[-1];
243 cx->fp->sp--;
244 if (SWFDEC_IS_SPRITE_MOVIE (cx->frame->target))
245 SwfdecMovie *movie = SWFDEC_MOVIE (cx->frame->target);
246 int frame = swfdec_value_to_frame (cx, movie, val);
247 guint jump = data[2];
248 guint loaded;
249 if (frame < 0)
250 return JS_TRUE;
251 if (SWFDEC_IS_ROOT_MOVIE (movie)) {
252 SwfdecDecoder *dec = SWFDEC_ROOT_MOVIE (movie)->decoder;
253 loaded = dec->frames_loaded;
254 g_assert (loaded <= movie->n_frames);
255 } else {
256 loaded = movie->n_frames;
258 if (loaded <= (guint) frame)
259 swfdec_script_skip_actions (cx, jump);
260 } else {
261 SWFDEC_ERROR ("no movie to WaitForFrame2 on");
263 return JS_TRUE;
265 #endif
267 static void
268 swfdec_action_wait_for_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
270 SwfdecSpriteMovie *movie;
271 guint frame, jump, loaded;
273 if (len != 3) {
274 SWFDEC_ERROR ("WaitForFrame action length invalid (is %u, should be 3", len);
275 return;
277 if (!SWFDEC_IS_SPRITE_MOVIE (cx->frame->target)) {
278 SWFDEC_ERROR ("no movie for WaitForFrame");
279 return;
282 movie = SWFDEC_SPRITE_MOVIE (cx->frame->target);
283 frame = data[0] || (data[1] << 8);
284 jump = data[2];
285 if (SWFDEC_MOVIE (movie)->swf->movie == movie) {
286 SwfdecDecoder *dec = SWFDEC_MOVIE (movie)->swf->decoder;
287 loaded = dec->frames_loaded;
288 g_assert (loaded <= movie->n_frames);
289 if (loaded == dec->frames_total)
290 loaded = G_MAXUINT;
291 } else {
292 loaded = G_MAXUINT;
294 if (loaded <= frame)
295 swfdec_script_skip_actions (cx, jump);
298 static void
299 swfdec_action_constant_pool (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
301 SwfdecConstantPool *pool;
302 SwfdecAsFrame *frame;
304 frame = cx->frame;
305 pool = swfdec_constant_pool_new_from_action (data, len, cx->version);
306 if (pool == NULL)
307 return;
308 swfdec_constant_pool_attach_to_context (pool, cx);
309 if (frame->constant_pool)
310 swfdec_constant_pool_free (frame->constant_pool);
311 frame->constant_pool = pool;
312 if (frame->constant_pool_buffer)
313 swfdec_buffer_unref (frame->constant_pool_buffer);
314 frame->constant_pool_buffer = swfdec_buffer_new_subbuffer (frame->script->buffer,
315 data - frame->script->buffer->data, len);
318 static void
319 swfdec_action_push (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
321 SwfdecBits bits;
323 swfdec_bits_init_data (&bits, data, len);
324 while (swfdec_bits_left (&bits)) {
325 guint type = swfdec_bits_get_u8 (&bits);
326 SWFDEC_LOG ("push type %u", type);
327 swfdec_as_stack_ensure_free (cx, 1);
328 switch (type) {
329 case 0: /* string */
331 char *s = swfdec_bits_get_string_with_version (&bits, cx->version);
332 if (s == NULL)
333 return;
334 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
335 swfdec_as_context_give_string (cx, s));
336 break;
338 case 1: /* float */
339 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_push (cx),
340 swfdec_bits_get_float (&bits));
341 break;
342 case 2: /* null */
343 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_push (cx));
344 break;
345 case 3: /* undefined */
346 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
347 break;
348 case 4: /* register */
350 guint regnum = swfdec_bits_get_u8 (&bits);
351 if (!swfdec_action_has_register (cx, regnum)) {
352 SWFDEC_ERROR ("cannot Push register %u: not enough registers", regnum);
353 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
354 } else {
355 *swfdec_as_stack_push (cx) = cx->frame->registers[regnum];
357 break;
359 case 5: /* boolean */
360 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_push (cx),
361 swfdec_bits_get_u8 (&bits) ? TRUE : FALSE);
362 break;
363 case 6: /* double */
364 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_push (cx),
365 swfdec_bits_get_double (&bits));
366 break;
367 case 7: /* 32bit int */
368 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_push (cx),
369 (int) swfdec_bits_get_u32 (&bits));
370 break;
371 case 8: /* 8bit ConstantPool address */
373 guint i = swfdec_bits_get_u8 (&bits);
374 SwfdecConstantPool *pool = cx->frame->constant_pool;
375 if (pool == NULL) {
376 SWFDEC_ERROR ("no constant pool to push from");
377 return;
379 if (i >= swfdec_constant_pool_size (pool)) {
380 SWFDEC_ERROR ("constant pool index %u too high - only %u elements",
381 i, swfdec_constant_pool_size (pool));
382 return;
384 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
385 swfdec_constant_pool_get (pool, i));
386 break;
388 case 9: /* 16bit ConstantPool address */
390 guint i = swfdec_bits_get_u16 (&bits);
391 SwfdecConstantPool *pool = cx->frame->constant_pool;
392 if (pool == NULL) {
393 SWFDEC_ERROR ("no constant pool to push from");
394 return;
396 if (i >= swfdec_constant_pool_size (pool)) {
397 SWFDEC_ERROR ("constant pool index %u too high - only %u elements",
398 i, swfdec_constant_pool_size (pool));
399 return;
401 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx),
402 swfdec_constant_pool_get (pool, i));
403 break;
405 default:
406 SWFDEC_ERROR ("Push: type %u not implemented", type);
407 return;
412 static void
413 swfdec_action_get_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
415 const char *s;
417 s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
418 swfdec_as_context_eval (cx, NULL, s, swfdec_as_stack_peek (cx, 1));
419 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
420 if (SWFDEC_AS_VALUE_IS_UNDEFINED (swfdec_as_stack_peek (cx, 1))) {
421 SWFDEC_WARNING ("no variable named %s", s);
423 #endif
426 static void
427 swfdec_action_set_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
429 const char *s;
431 s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
432 swfdec_as_context_eval_set (cx, NULL, s, swfdec_as_stack_peek (cx, 1));
433 swfdec_as_stack_pop_n (cx, 2);
436 static const char *
437 swfdec_as_interpret_eval (SwfdecAsContext *cx, SwfdecAsObject *obj,
438 SwfdecAsValue *val)
440 if (SWFDEC_AS_VALUE_IS_STRING (val)) {
441 const char *s = SWFDEC_AS_VALUE_GET_STRING (val);
442 if (s != SWFDEC_AS_STR_EMPTY) {
443 swfdec_as_context_eval (cx, obj, s, val);
444 return s;
447 if (obj != NULL)
448 SWFDEC_AS_VALUE_SET_OBJECT (val, obj);
449 else
450 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
451 return SWFDEC_AS_STR_EMPTY;
454 /* FIXME: this sucks */
455 extern struct {
456 gboolean needs_movie;
457 const char * name; /* GC'd */
458 void (* get) (SwfdecMovie *movie, SwfdecAsValue *ret);
459 void (* set) (SwfdecMovie *movie, const SwfdecAsValue *val);
460 } swfdec_movieclip_props[];
461 static void
462 swfdec_action_get_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
464 SwfdecAsValue *val;
465 SwfdecAsObject *obj;
466 guint id;
468 id = swfdec_as_value_to_integer (cx, swfdec_as_stack_pop (cx));
469 if (id > (cx->version > 4 ? 21 : 18)) {
470 SWFDEC_WARNING ("trying to SetProperty %u, not allowed", id);
471 goto out;
473 val = swfdec_as_stack_peek (cx, 1);
474 swfdec_as_interpret_eval (cx, NULL, val);
475 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val)) {
476 obj = cx->frame->target;
477 } else if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
478 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
479 } else {
480 SWFDEC_WARNING ("not an object, can't GetProperty");
481 goto out;
483 swfdec_as_object_get_variable (obj, swfdec_movieclip_props[id].name,
484 swfdec_as_stack_peek (cx, 1));
485 return;
487 out:
488 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
491 static void
492 swfdec_action_set_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
494 SwfdecAsValue *val;
495 SwfdecAsObject *obj;
496 guint id;
498 id = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
499 if (id > (cx->version > 4 ? 21 : 18)) {
500 SWFDEC_WARNING ("trying to SetProperty %u, not allowed", id);
501 goto out;
503 val = swfdec_as_stack_peek (cx, 3);
504 swfdec_as_interpret_eval (cx, NULL, val);
505 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val)) {
506 obj = cx->frame->target;
507 } else if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
508 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
509 } else {
510 SWFDEC_WARNING ("not an object, can't get SetProperty");
511 goto out;
513 swfdec_as_object_set_variable (obj, swfdec_movieclip_props[id].name,
514 swfdec_as_stack_peek (cx, 1));
515 out:
516 swfdec_as_stack_pop_n (cx, 3);
519 static void
520 swfdec_action_get_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
522 SwfdecAsObject *object = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 2));
523 if (object) {
524 const char *name;
525 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
526 swfdec_as_object_get_variable (object, name, swfdec_as_stack_peek (cx, 2));
527 #ifdef SWFDEC_WARN_MISSING_PROPERTIES
528 if (SWFDEC_AS_VALUE_IS_UNDEFINED (swfdec_as_stack_peek (cx, 2))) {
529 SWFDEC_WARNING ("no variable named %s:%s", G_OBJECT_TYPE_NAME (object), name);
531 #endif
532 } else {
533 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
535 swfdec_as_stack_pop (cx);
538 static void
539 swfdec_action_set_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
541 if (SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 3))) {
542 const char *name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
543 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 3)),
544 name, swfdec_as_stack_peek (cx, 1));
546 swfdec_as_stack_pop_n (cx, 3);
549 static void
550 swfdec_action_trace (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
552 SwfdecAsValue *val;
553 const char *s;
555 val = swfdec_as_stack_peek (cx, 1);
556 if (val->type == SWFDEC_AS_TYPE_UNDEFINED) {
557 s = SWFDEC_AS_STR_undefined;
558 } else if (val->type == SWFDEC_AS_TYPE_OBJECT &&
559 SWFDEC_IS_AS_STRING (swfdec_as_value_to_object (cx, val))) {
560 s = SWFDEC_AS_STRING (swfdec_as_value_to_object (cx, val))->string;
561 } else {
562 s = swfdec_as_value_to_string (cx, val);
564 swfdec_as_stack_pop (cx);
565 g_signal_emit_by_name (cx, "trace", s);
568 /* stack looks like this: [ function, this, arg1, arg2, ... ] */
569 /* stack must be at least 2 elements big */
570 static gboolean
571 swfdec_action_call (SwfdecAsContext *cx, guint n_args)
573 SwfdecAsFunction *fun;
574 SwfdecAsObject *thisp;
576 if (!SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 1)))
577 goto error;
578 fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 1));
579 if (!SWFDEC_IS_AS_FUNCTION (fun))
580 goto error;
581 if (!SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 2))) {
582 thisp = NULL;
583 } else {
584 thisp = SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 2));
586 swfdec_as_stack_pop_n (cx, 2);
587 /* sanitize argument count */
588 if (n_args >= swfdec_as_stack_get_size (cx))
589 n_args = swfdec_as_stack_get_size (cx);
590 swfdec_as_function_call (fun, thisp, n_args, NULL, NULL);
591 if (SWFDEC_IS_AS_SUPER (fun)) {
592 SWFDEC_LOG ("replacing super object on frame");
593 swfdec_as_super_replace (SWFDEC_AS_SUPER (fun), NULL);
595 return TRUE;
597 error:
598 n_args += 2;
599 if (n_args > swfdec_as_stack_get_size (cx))
600 n_args = swfdec_as_stack_get_size (cx);
601 swfdec_as_stack_pop_n (cx, n_args);
602 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_push (cx));
603 return FALSE;
606 static void
607 swfdec_action_call_function (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
609 SwfdecAsFrame *frame = cx->frame;
610 SwfdecAsObject *obj;
611 guint n_args;
612 const char *name;
613 SwfdecAsValue *fun, *thisp;
615 swfdec_as_stack_ensure_size (cx, 2);
616 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
617 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
618 thisp = swfdec_as_stack_peek (cx, 2);
619 fun = swfdec_as_stack_peek (cx, 1);
620 obj = swfdec_as_frame_find_variable (frame, name);
621 if (obj) {
622 swfdec_as_object_get_variable (obj, name, fun);
623 SWFDEC_AS_VALUE_SET_OBJECT (thisp, obj);
624 } else {
625 SWFDEC_AS_VALUE_SET_NULL (thisp);
626 SWFDEC_AS_VALUE_SET_UNDEFINED (fun);
628 if (!swfdec_action_call (cx, n_args)) {
629 SWFDEC_ERROR ("no function named %s", name);
633 static void
634 swfdec_action_call_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
636 SwfdecAsFrame *frame = cx->frame;
637 SwfdecAsValue *val;
638 SwfdecAsObject *obj;
639 guint n_args;
640 const char *name = NULL;
642 swfdec_as_stack_ensure_size (cx, 3);
643 obj = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 2));
644 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 3));
645 val = swfdec_as_stack_peek (cx, 1);
646 /* FIXME: this is a hack for constructors calling super - is this correct? */
647 if (SWFDEC_AS_VALUE_IS_UNDEFINED (val)) {
648 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
650 if (obj) {
651 if (SWFDEC_AS_VALUE_IS_STRING (val) &&
652 SWFDEC_AS_VALUE_GET_STRING (val) == SWFDEC_AS_STR_EMPTY) {
653 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 3));
654 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 2), obj);
655 } else {
656 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 3), obj);
657 name = swfdec_as_value_to_string (cx, val);
658 swfdec_as_object_get_variable (obj, name, swfdec_as_stack_peek (cx, 2));
660 } else {
661 if (SWFDEC_AS_VALUE_IS_STRING (val))
662 name = SWFDEC_AS_VALUE_GET_STRING (val);
663 SWFDEC_AS_VALUE_SET_NULL (swfdec_as_stack_peek (cx, 3));
664 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 2));
666 swfdec_as_stack_pop (cx);
667 if (swfdec_action_call (cx, n_args)) {
668 /* setup super to point to the right prototype */
669 frame = cx->frame;
670 if (SWFDEC_IS_AS_SUPER (obj)) {
671 swfdec_as_super_replace (SWFDEC_AS_SUPER (obj), name);
672 } else if (frame->super) {
673 SwfdecAsSuper *super = SWFDEC_AS_SUPER (frame->super);
674 if (name &&
675 cx->version > 6 &&
676 swfdec_as_object_get_variable_and_flags (frame->thisp,
677 name, NULL, NULL, &super->object) &&
678 super->object == frame->thisp) {
679 super->object = super->object->prototype;
682 } else {
683 SWFDEC_ERROR ("no function named %s on object %s", name ? name : "unknown", obj ? G_OBJECT_TYPE_NAME(obj) : "unknown");
687 static void
688 swfdec_action_pop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
690 swfdec_as_stack_pop (cx);
693 static void
694 swfdec_action_binary (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
696 double l, r;
698 r = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
699 l = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
700 switch (action) {
701 case SWFDEC_AS_ACTION_ADD:
702 l = l + r;
703 break;
704 case SWFDEC_AS_ACTION_SUBTRACT:
705 l = l - r;
706 break;
707 case SWFDEC_AS_ACTION_MULTIPLY:
708 l = l * r;
709 break;
710 case SWFDEC_AS_ACTION_DIVIDE:
711 if (cx->version < 5) {
712 if (r == 0) {
713 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), SWFDEC_AS_STR__ERROR_);
714 return;
717 if (r == 0) {
718 if (l > 0)
719 l = INFINITY;
720 else if (l < 0)
721 l = -INFINITY;
722 else
723 l = NAN;
724 } else {
725 l = l / r;
727 break;
728 default:
729 g_assert_not_reached ();
730 break;
732 swfdec_as_stack_pop (cx);
733 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), l);
736 static void
737 swfdec_action_add2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
739 SwfdecAsValue *rval, *lval, rtmp, ltmp;
741 rval = swfdec_as_stack_peek (cx, 1);
742 lval = swfdec_as_stack_peek (cx, 2);
743 rtmp = *rval;
744 ltmp = *lval;
745 swfdec_as_value_to_primitive (&rtmp);
746 if (!SWFDEC_AS_VALUE_IS_OBJECT (&rtmp) || SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&rtmp)))
747 rval = &rtmp;
748 swfdec_as_value_to_primitive (&ltmp);
749 if (!SWFDEC_AS_VALUE_IS_OBJECT (&ltmp) || SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&ltmp)))
750 lval = &ltmp;
752 if (SWFDEC_AS_VALUE_IS_STRING (lval) || SWFDEC_AS_VALUE_IS_STRING (rval)) {
753 const char *lstr, *rstr;
754 lstr = swfdec_as_value_to_string (cx, lval);
755 rstr = swfdec_as_value_to_string (cx, rval);
756 lstr = swfdec_as_str_concat (cx, lstr, rstr);
757 swfdec_as_stack_pop (cx);
758 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1), lstr);
759 } else {
760 double d, d2;
761 d = swfdec_as_value_to_number (cx, lval);
762 d2 = swfdec_as_value_to_number (cx, rval);
763 d += d2;
764 swfdec_as_stack_pop (cx);
765 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), d);
769 static void
770 swfdec_action_new_comparison (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
772 SwfdecAsValue *lval, *rval;
773 double l, r;
775 rval = swfdec_as_stack_peek (cx, 1);
776 lval = swfdec_as_stack_peek (cx, 2);
778 /* swap if we do a greater comparison */
779 if (action == SWFDEC_AS_ACTION_GREATER) {
780 SwfdecAsValue *tmp = lval;
781 lval = rval;
782 rval = tmp;
784 /* comparison with object is always false */
785 swfdec_as_value_to_primitive (lval);
786 if (SWFDEC_AS_VALUE_IS_OBJECT (lval) &&
787 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (lval))) {
788 swfdec_as_stack_pop (cx);
789 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
790 return;
792 /* same for the rval */
793 swfdec_as_value_to_primitive (rval);
794 if (SWFDEC_AS_VALUE_IS_OBJECT (rval) &&
795 !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (rval))) {
796 swfdec_as_stack_pop (cx);
797 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), FALSE);
798 return;
800 /* movieclips are not objects, but they evaluate to NaN, so we can handle them here */
801 if (SWFDEC_AS_VALUE_IS_OBJECT (rval) ||
802 SWFDEC_AS_VALUE_IS_OBJECT (lval)) {
803 swfdec_as_stack_pop (cx);
804 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
805 return;
807 /* if both are strings, compare strings */
808 if (SWFDEC_AS_VALUE_IS_STRING (rval) &&
809 SWFDEC_AS_VALUE_IS_STRING (lval)) {
810 const char *ls = SWFDEC_AS_VALUE_GET_STRING (lval);
811 const char *rs = SWFDEC_AS_VALUE_GET_STRING (rval);
812 int cmp;
813 if (ls == SWFDEC_AS_STR_EMPTY) {
814 cmp = rs == SWFDEC_AS_STR_EMPTY ? 0 : 1;
815 } else if (rs == SWFDEC_AS_STR_EMPTY) {
816 cmp = -1;
817 } else {
818 cmp = strcmp (ls, rs);
820 swfdec_as_stack_pop (cx);
821 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cmp < 0);
822 return;
824 /* convert to numbers and compare those */
825 l = swfdec_as_value_to_number (cx, lval);
826 r = swfdec_as_value_to_number (cx, rval);
827 swfdec_as_stack_pop (cx);
828 /* NaN results in undefined */
829 if (isnan (l) || isnan (r)) {
830 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
831 return;
833 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), l < r);
836 static void
837 swfdec_action_not_4 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
839 double d;
841 d = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
842 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), d == 0 ? 1 : 0);
845 static void
846 swfdec_action_not_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
848 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1),
849 !swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1)));
852 static void
853 swfdec_action_jump (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
855 if (len != 2) {
856 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
857 return;
859 cx->frame->pc += 5 + GINT16_FROM_LE (*((gint16*) data));
862 static void
863 swfdec_action_if (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
865 if (len != 2) {
866 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
867 return;
869 if (swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1)))
870 cx->frame->pc += 5 + GINT16_FROM_LE (*((gint16*) data));
871 swfdec_as_stack_pop (cx);
874 static void
875 swfdec_action_decrement (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
877 SwfdecAsValue *val;
879 val = swfdec_as_stack_peek (cx, 1);
880 SWFDEC_AS_VALUE_SET_NUMBER (val, swfdec_as_value_to_number (cx, val) - 1);
883 static void
884 swfdec_action_increment (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
886 SwfdecAsValue *val;
888 val = swfdec_as_stack_peek (cx, 1);
889 SWFDEC_AS_VALUE_SET_NUMBER (val, swfdec_as_value_to_number (cx, val) + 1);
892 static void
893 swfdec_action_get_url (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
895 SwfdecBits bits;
896 char *url, *target;
898 swfdec_bits_init_data (&bits, data, len);
899 url = swfdec_bits_get_string_with_version (&bits, cx->version);
900 target = swfdec_bits_get_string_with_version (&bits, cx->version);
901 if (url == NULL || target == NULL) {
902 SWFDEC_ERROR ("not enough data in GetURL");
903 g_free (url);
904 g_free (target);
905 return;
907 if (swfdec_bits_left (&bits)) {
908 SWFDEC_WARNING ("leftover bytes in GetURL action");
910 if (SWFDEC_IS_MOVIE (cx->frame->target))
911 swfdec_movie_load (SWFDEC_MOVIE (cx->frame->target), url, target,
912 SWFDEC_LOADER_REQUEST_DEFAULT, NULL, 0);
913 else
914 SWFDEC_WARNING ("no movie to load");
915 g_free (url);
916 g_free (target);
919 static void
920 swfdec_action_get_url2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
922 const char *target, *url;
923 guint method;
925 if (len != 1) {
926 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
927 return;
929 target = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
930 url = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
931 method = data[0] >> 6;
932 if (method == 3) {
933 SWFDEC_ERROR ("GetURL method 3 invalid");
934 method = 0;
936 if (data[0] & 2) {
937 SWFDEC_FIXME ("implement LoadTarget");
939 if (data[0] & 1) {
940 SWFDEC_FIXME ("implement LoadVariables");
942 if (SWFDEC_IS_MOVIE (cx->frame->target))
943 swfdec_movie_load (SWFDEC_MOVIE (cx->frame->target), url, target, method, NULL, 0);
944 else
945 SWFDEC_WARNING ("no movie to load");
946 swfdec_as_stack_pop_n (cx, 2);
949 static void
950 swfdec_action_string_add (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
952 const char *lval, *rval;
954 rval = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
955 lval = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
956 lval = swfdec_as_str_concat (cx, lval, rval);
957 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 2), lval);
958 swfdec_as_stack_pop (cx);
961 static void
962 swfdec_action_push_duplicate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
964 *swfdec_as_stack_push (cx) = *swfdec_as_stack_peek (cx, 1);
967 static void
968 swfdec_action_random_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
970 gint32 max;
971 SwfdecAsValue *val;
973 val = swfdec_as_stack_peek (cx, 1);
974 max = swfdec_as_value_to_integer (cx, val);
976 if (max <= 0)
977 SWFDEC_AS_VALUE_SET_NUMBER (val, 0);
978 else
979 SWFDEC_AS_VALUE_SET_NUMBER (val, g_rand_int_range (cx->rand, 0, max));
982 static void
983 swfdec_action_old_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
985 double l, r;
986 gboolean cond;
988 l = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
989 r = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
990 switch (action) {
991 case SWFDEC_AS_ACTION_EQUALS:
992 cond = l == r;
993 break;
994 case SWFDEC_AS_ACTION_LESS:
995 cond = l < r;
996 break;
997 default:
998 g_assert_not_reached ();
999 return;
1001 swfdec_as_stack_pop (cx);
1002 if (cx->version < 5) {
1003 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), cond ? 1 : 0);
1004 } else {
1005 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1009 static void
1010 swfdec_action_string_length (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1012 const char *s;
1013 SwfdecAsValue *v;
1015 v = swfdec_as_stack_peek (cx, 1);
1016 s = swfdec_as_value_to_string (cx, v);
1017 SWFDEC_AS_VALUE_SET_INT (v, g_utf8_strlen (s, -1));
1020 static void
1021 swfdec_action_string_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1023 const char *l, *r;
1024 gboolean cond;
1026 r = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1027 l = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1028 switch (action) {
1029 case SWFDEC_AS_ACTION_STRING_EQUALS:
1030 cond = l == r;
1031 break;
1032 case SWFDEC_AS_ACTION_STRING_LESS:
1033 cond = strcmp (l, r) < 0;
1034 break;
1035 default:
1036 g_assert_not_reached ();
1037 break;
1039 swfdec_as_stack_pop (cx);
1040 if (cx->version < 5) {
1041 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), cond ? 1 : 0);
1042 } else {
1043 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1047 static void
1048 swfdec_action_equals2_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1050 SwfdecAsValue *rval, *lval;
1051 SwfdecAsValue rtmp, ltmp;
1052 SwfdecAsValueType ltype, rtype;
1053 double l, r;
1054 gboolean cond;
1056 rval = swfdec_as_stack_peek (cx, 1);
1057 lval = swfdec_as_stack_peek (cx, 2);
1058 rtmp = *rval;
1059 ltmp = *lval;
1060 swfdec_as_value_to_primitive (&rtmp);
1061 swfdec_as_value_to_primitive (&ltmp);
1062 ltype = ltmp.type;
1063 rtype = rtmp.type;
1065 /* get objects compared */
1066 if (ltype == SWFDEC_AS_TYPE_OBJECT && rtype == SWFDEC_AS_TYPE_OBJECT) {
1067 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (&ltmp);
1068 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (&rtmp);
1070 if (!SWFDEC_IS_MOVIE (lo))
1071 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1072 if (!SWFDEC_IS_MOVIE (ro))
1073 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1075 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1076 /* do nothing */
1077 } else if (SWFDEC_IS_MOVIE (lo)) {
1078 swfdec_as_value_to_primitive (rval);
1079 rtype = rval->type;
1080 if (rtype != SWFDEC_AS_TYPE_OBJECT) {
1081 cond = FALSE;
1082 goto out;
1084 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1085 } else if (SWFDEC_IS_MOVIE (ro)) {
1086 swfdec_as_value_to_primitive (lval);
1087 ltype = lval->type;
1088 if (ltype != SWFDEC_AS_TYPE_OBJECT) {
1089 cond = FALSE;
1090 goto out;
1092 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1094 cond = lo == ro;
1095 goto out;
1098 /* compare strings */
1099 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1100 /* FIXME: flash 5 case insensitive? */
1101 cond = SWFDEC_AS_VALUE_GET_STRING (&ltmp) == SWFDEC_AS_VALUE_GET_STRING (&rtmp);
1102 goto out;
1105 /* convert to numbers */
1106 if (SWFDEC_AS_VALUE_IS_OBJECT (&ltmp) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&ltmp))) {
1107 l = swfdec_as_value_to_number (cx, lval);
1108 } else {
1109 l = swfdec_as_value_to_number (cx, &ltmp);
1111 if (SWFDEC_AS_VALUE_IS_OBJECT (&rtmp) && !SWFDEC_IS_MOVIE (SWFDEC_AS_VALUE_GET_OBJECT (&rtmp))) {
1112 r = swfdec_as_value_to_number (cx, rval);
1113 } else {
1114 r = swfdec_as_value_to_number (cx, &rtmp);
1117 /* get rid of undefined and null */
1118 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1119 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1120 goto out;
1121 } else if (cond) {
1122 cond = FALSE;
1123 goto out;
1126 /* else compare as numbers */
1127 if (isnan (l) && isnan (r))
1128 cond = ltype == rtype;
1129 else
1130 cond = l == r;
1132 out:
1133 swfdec_as_stack_pop (cx);
1134 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1137 static void
1138 swfdec_action_equals2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1140 SwfdecAsValue *rval, *lval;
1141 SwfdecAsValueType ltype, rtype;
1142 double l, r;
1143 gboolean cond;
1145 rval = swfdec_as_stack_peek (cx, 1);
1146 lval = swfdec_as_stack_peek (cx, 2);
1147 ltype = lval->type;
1148 rtype = rval->type;
1150 /* get objects compared */
1151 if (ltype == SWFDEC_AS_TYPE_OBJECT && rtype == SWFDEC_AS_TYPE_OBJECT) {
1152 SwfdecAsObject *lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1153 SwfdecAsObject *ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1155 if (SWFDEC_IS_MOVIE (lo) && SWFDEC_IS_MOVIE (ro)) {
1156 /* do nothing */
1157 } else if (SWFDEC_IS_MOVIE (lo)) {
1158 swfdec_as_value_to_primitive (rval);
1159 rtype = rval->type;
1160 if (rtype != SWFDEC_AS_TYPE_OBJECT) {
1161 cond = FALSE;
1162 goto out;
1164 ro = SWFDEC_AS_VALUE_GET_OBJECT (rval);
1165 } else if (SWFDEC_IS_MOVIE (ro)) {
1166 swfdec_as_value_to_primitive (lval);
1167 ltype = lval->type;
1168 if (ltype != SWFDEC_AS_TYPE_OBJECT) {
1169 cond = FALSE;
1170 goto out;
1172 lo = SWFDEC_AS_VALUE_GET_OBJECT (lval);
1174 cond = lo == ro;
1175 goto out;
1178 /* if one of the values is an object, call valueOf.
1179 * If it's still an object, return FALSE */
1180 swfdec_as_value_to_primitive (lval);
1181 ltype = lval->type;
1182 if (ltype == SWFDEC_AS_TYPE_OBJECT) {
1183 cond = FALSE;
1184 goto out;
1186 swfdec_as_value_to_primitive (rval);
1187 rtype = rval->type;
1188 if (rtype == SWFDEC_AS_TYPE_OBJECT) {
1189 cond = FALSE;
1190 goto out;
1192 /* now we have a comparison without objects */
1194 /* get rid of undefined and null */
1195 cond = rtype == SWFDEC_AS_TYPE_UNDEFINED || rtype == SWFDEC_AS_TYPE_NULL;
1196 if (ltype == SWFDEC_AS_TYPE_UNDEFINED || ltype == SWFDEC_AS_TYPE_NULL) {
1197 goto out;
1198 } else if (cond) {
1199 cond = FALSE;
1200 goto out;
1203 /* compare strings */
1204 if (ltype == SWFDEC_AS_TYPE_STRING && rtype == SWFDEC_AS_TYPE_STRING) {
1205 /* FIXME: flash 5 case insensitive? */
1206 cond = SWFDEC_AS_VALUE_GET_STRING (lval) == SWFDEC_AS_VALUE_GET_STRING (rval);
1207 goto out;
1210 /* else compare as numbers */
1211 l = swfdec_as_value_to_number (cx, lval);
1212 r = swfdec_as_value_to_number (cx, rval);
1214 if (isnan (l) && isnan (r))
1215 cond = ltype == rtype;
1216 else
1217 cond = l == r;
1219 out:
1220 swfdec_as_stack_pop (cx);
1221 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1224 static void
1225 swfdec_action_strict_equals (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1227 SwfdecAsValue *rval, *lval;
1228 gboolean cond;
1230 rval = swfdec_as_stack_peek (cx, 1);
1231 lval = swfdec_as_stack_peek (cx, 2);
1233 if (rval->type != lval->type) {
1234 cond = FALSE;
1235 } else {
1236 switch (rval->type) {
1237 case SWFDEC_AS_TYPE_UNDEFINED:
1238 case SWFDEC_AS_TYPE_NULL:
1239 cond = TRUE;
1240 break;
1241 case SWFDEC_AS_TYPE_BOOLEAN:
1242 cond = SWFDEC_AS_VALUE_GET_BOOLEAN (rval) == SWFDEC_AS_VALUE_GET_BOOLEAN (lval);
1243 break;
1244 case SWFDEC_AS_TYPE_NUMBER:
1246 double l, r;
1247 r = SWFDEC_AS_VALUE_GET_NUMBER (rval);
1248 l = SWFDEC_AS_VALUE_GET_NUMBER (lval);
1249 cond = (l == r) || (isnan (l) && isnan (r));
1251 break;
1252 case SWFDEC_AS_TYPE_STRING:
1253 cond = SWFDEC_AS_VALUE_GET_STRING (rval) == SWFDEC_AS_VALUE_GET_STRING (lval);
1254 break;
1255 case SWFDEC_AS_TYPE_OBJECT:
1256 cond = SWFDEC_AS_VALUE_GET_OBJECT (rval) == SWFDEC_AS_VALUE_GET_OBJECT (lval);
1257 break;
1258 default:
1259 g_assert_not_reached ();
1260 cond = FALSE;
1264 swfdec_as_stack_pop (cx);
1265 SWFDEC_AS_VALUE_SET_BOOLEAN (swfdec_as_stack_peek (cx, 1), cond);
1268 static void
1269 swfdec_action_set_target (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1271 if (!memchr (data, 0, len)) {
1272 SWFDEC_ERROR ("SetTarget action does not specify a string");
1273 return;
1275 if (*data == '\0') {
1276 swfdec_as_frame_set_target (cx->frame, NULL);
1277 } else {
1278 SwfdecAsValue target;
1279 swfdec_as_context_eval (cx, NULL, (const char *) data, &target);
1280 if (!SWFDEC_AS_VALUE_IS_OBJECT (&target)) {
1281 SWFDEC_WARNING ("target is not an object");
1282 return;
1284 /* FIXME: allow non-movieclips as targets? */
1285 swfdec_as_frame_set_target (cx->frame, SWFDEC_AS_VALUE_GET_OBJECT (&target));
1289 static void
1290 swfdec_action_set_target2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1292 SwfdecAsValue *val;
1293 val = swfdec_as_stack_peek (cx, 1);
1294 if (!SWFDEC_AS_VALUE_IS_OBJECT (val)) {
1295 SWFDEC_WARNING ("target is not an object");
1296 } else {
1297 /* FIXME: allow non-movieclips as targets? */
1298 swfdec_as_frame_set_target (cx->frame, SWFDEC_AS_VALUE_GET_OBJECT (val));
1300 swfdec_as_stack_pop (cx);
1303 static void
1304 swfdec_action_start_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1306 SwfdecRect rect, *rectp = NULL;
1307 SwfdecMovie *movie;
1308 gboolean center;
1309 guint stack_size = 3;
1311 swfdec_as_stack_ensure_size (cx, 3);
1312 if (swfdec_as_interpret_eval (cx, NULL, swfdec_as_stack_peek (cx, 1)) == SWFDEC_AS_STR_EMPTY) {
1313 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_peek (cx, 1), cx->frame->target);
1315 center = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 2));
1316 if (swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 3))) {
1317 swfdec_as_stack_ensure_size (cx, 7);
1318 rect.x0 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 7));
1319 rect.y0 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 6));
1320 rect.x1 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 5));
1321 rect.y1 = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 4));
1322 swfdec_rect_scale (&rect, &rect, SWFDEC_TWIPS_SCALE_FACTOR);
1323 stack_size = 7;
1324 rectp = &rect;
1326 if (SWFDEC_AS_VALUE_IS_OBJECT (swfdec_as_stack_peek (cx, 1)) &&
1327 SWFDEC_IS_MOVIE (movie = (SwfdecMovie *) SWFDEC_AS_VALUE_GET_OBJECT (swfdec_as_stack_peek (cx, 1)))) {
1328 swfdec_player_set_drag_movie (SWFDEC_PLAYER (cx), movie, center, &rect);
1329 } else {
1330 SWFDEC_ERROR ("startDrag on something not a Movie");
1332 swfdec_as_stack_pop_n (cx, stack_size);
1335 static void
1336 swfdec_action_end_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1338 if (SWFDEC_IS_PLAYER (cx)) {
1339 swfdec_player_set_drag_movie (SWFDEC_PLAYER (cx), NULL, FALSE, NULL);
1340 } else {
1341 SWFDEC_WARNING ("can't end a drag on non-players");
1345 static void
1346 swfdec_action_stop_sounds (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1348 if (SWFDEC_IS_PLAYER (cx)) {
1349 swfdec_player_stop_all_sounds (SWFDEC_PLAYER (cx));
1353 static void
1354 swfdec_action_new_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1356 SwfdecAsValue *constructor;
1357 SwfdecAsFunction *fun;
1358 guint n_args;
1359 const char *name;
1361 swfdec_as_stack_ensure_size (cx, 2);
1362 constructor = swfdec_as_stack_peek (cx, 1);
1363 name = swfdec_as_interpret_eval (cx, NULL, constructor);
1364 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1365 n_args = MIN (swfdec_as_stack_get_size (cx) - 2, n_args);
1366 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor) ||
1367 !SWFDEC_IS_AS_FUNCTION (fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (constructor))) {
1368 SWFDEC_WARNING ("%s is not a constructor", name);
1369 goto fail;
1372 swfdec_as_stack_pop_n (cx, 2);
1373 swfdec_as_object_create (fun, n_args, NULL);
1374 return;
1376 fail:
1377 swfdec_as_stack_pop_n (cx, n_args + 1);
1378 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1381 static void
1382 swfdec_action_new_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1384 SwfdecAsValue *constructor;
1385 SwfdecAsFunction *fun;
1386 guint n_args;
1387 const char *name;
1389 swfdec_as_stack_ensure_size (cx, 3);
1390 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1392 constructor = swfdec_as_stack_peek (cx, 2);
1393 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 3));
1394 n_args = MIN (swfdec_as_stack_get_size (cx) - 3, n_args);
1395 if (name != SWFDEC_AS_STR_EMPTY) {
1396 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor)) {
1397 SWFDEC_WARNING ("NewMethod called without an object to get variable %s from", name);
1398 goto fail;
1400 swfdec_as_object_get_variable (SWFDEC_AS_VALUE_GET_OBJECT (constructor),
1401 name, constructor);
1403 if (!SWFDEC_AS_VALUE_IS_OBJECT (constructor) ||
1404 !SWFDEC_IS_AS_FUNCTION (fun = (SwfdecAsFunction *) SWFDEC_AS_VALUE_GET_OBJECT (constructor))) {
1405 SWFDEC_WARNING ("%s is not a constructor", name);
1406 goto fail;
1409 swfdec_as_stack_pop_n (cx, 3);
1410 swfdec_as_object_create (fun, n_args, NULL);
1411 return;
1413 fail:
1414 swfdec_as_stack_pop_n (cx, n_args + 2);
1415 SWFDEC_AS_VALUE_SET_UNDEFINED (swfdec_as_stack_peek (cx, 1));
1418 static void
1419 swfdec_action_init_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1421 SwfdecAsObject *object;
1422 guint i, n_args, size;
1424 n_args = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1425 swfdec_as_stack_pop (cx);
1426 if (n_args * 2 > swfdec_as_stack_get_size (cx)) {
1427 size = swfdec_as_stack_get_size (cx);
1428 SWFDEC_FIXME ("InitObject action with too small stack, help!");
1429 n_args = size / 2;
1430 size &= 1;
1431 } else {
1432 size = 0;
1435 object = swfdec_as_object_new (cx);
1436 if (object == NULL)
1437 return;
1438 for (i = 0; i < n_args; i++) {
1439 const char *s = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1440 swfdec_as_object_set_variable (object, s, swfdec_as_stack_peek (cx, 1));
1441 swfdec_as_stack_pop_n (cx, 2);
1443 swfdec_as_stack_pop_n (cx, size);
1444 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), object);
1447 static void
1448 swfdec_action_init_array (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1450 int i, n;
1451 SwfdecAsObject *array;
1453 swfdec_as_stack_ensure_size (cx, 1);
1454 n = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1455 swfdec_as_stack_pop (cx);
1456 array = swfdec_as_array_new (cx);
1457 if (array == NULL)
1458 return;
1459 /* NB: we can't increase the stack here, as the number can easily be MAXINT */
1460 for (i = 0; i < n && swfdec_as_stack_get_size (cx) > 0; i++) {
1461 swfdec_as_stack_ensure_size (cx, 1);
1462 swfdec_as_array_push (SWFDEC_AS_ARRAY (array), swfdec_as_stack_pop (cx));
1464 if (i != n) {
1465 SwfdecAsValue val;
1466 SWFDEC_AS_VALUE_SET_INT (&val, i);
1467 swfdec_as_object_set_variable (array, SWFDEC_AS_STR_length, &val);
1469 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), array);
1472 static void
1473 swfdec_action_define_function (SwfdecAsContext *cx, guint action,
1474 const guint8 *data, guint len)
1476 char *function_name;
1477 const char *name = NULL;
1478 guint i, n_args, size, n_registers;
1479 SwfdecBuffer *buffer;
1480 SwfdecBits bits;
1481 SwfdecAsFunction *fun;
1482 SwfdecAsFrame *frame;
1483 SwfdecScript *script;
1484 guint flags = 0;
1485 SwfdecScriptArgument *args;
1486 gboolean v2 = (action == 0x8e);
1488 frame = cx->frame;
1489 swfdec_bits_init_data (&bits, data, len);
1490 function_name = swfdec_bits_get_string_with_version (&bits, cx->version);
1491 if (function_name == NULL) {
1492 SWFDEC_ERROR ("could not parse function name");
1493 return;
1495 n_args = swfdec_bits_get_u16 (&bits);
1496 if (v2) {
1497 n_registers = swfdec_bits_get_u8 (&bits);
1498 if (n_registers == 0)
1499 n_registers = 4;
1500 flags = swfdec_bits_get_u16 (&bits);
1501 } else {
1502 n_registers = 5;
1504 if (n_args) {
1505 args = g_new0 (SwfdecScriptArgument, n_args);
1506 for (i = 0; i < n_args && swfdec_bits_left (&bits); i++) {
1507 if (v2) {
1508 args[i].preload = swfdec_bits_get_u8 (&bits);
1509 if (args[i].preload && args[i].preload >= n_registers) {
1510 SWFDEC_ERROR ("argument %u cannot be preloaded into register %u out of %u",
1511 i, args[i].preload, n_registers);
1512 /* FIXME: figure out correct error handling here */
1513 args[i].preload = 0;
1516 args[i].name = swfdec_bits_get_string_with_version (&bits, cx->version);
1517 if (args[i].name == NULL || args[i].name == '\0') {
1518 SWFDEC_ERROR ("empty argument name not allowed");
1519 g_free (args);
1520 return;
1522 /* FIXME: check duplicate arguments */
1524 } else {
1525 args = NULL;
1527 size = swfdec_bits_get_u16 (&bits);
1528 /* check the script can be created */
1529 if (frame->script->buffer->data + frame->script->buffer->length < frame->pc + 3 + len + size) {
1530 SWFDEC_ERROR ("size of function is too big");
1531 g_free (args);
1532 g_free (function_name);
1533 return;
1535 /* create the script */
1536 buffer = swfdec_buffer_new_subbuffer (frame->script->buffer,
1537 frame->pc + 3 + len - frame->script->buffer->data, size);
1538 swfdec_bits_init (&bits, buffer);
1539 if (*function_name) {
1540 name = function_name;
1541 } else if (swfdec_as_stack_get_size (cx) > 0) {
1542 /* This is kind of a hack that uses a feature of the Adobe compiler:
1543 * foo = function () {} is compiled as these actions:
1544 * Push "foo", DefineFunction, SetVariable/SetMember
1545 * With this knowledge we can inspect the topmost stack member, since
1546 * it will contain the name this function will soon be assigned to.
1548 if (SWFDEC_AS_VALUE_IS_STRING (swfdec_as_stack_peek (cx, 1)))
1549 name = SWFDEC_AS_VALUE_GET_STRING (swfdec_as_stack_peek (cx, 1));
1551 if (name == NULL)
1552 name = "unnamed_function";
1553 script = swfdec_script_new_from_bits (&bits, name, cx->version);
1554 swfdec_buffer_unref (buffer);
1555 if (script == NULL) {
1556 SWFDEC_ERROR ("failed to create script");
1557 g_free (args);
1558 g_free (function_name);
1559 return;
1561 if (frame->constant_pool_buffer)
1562 script->constant_pool = swfdec_buffer_ref (frame->constant_pool_buffer);
1563 script->flags = flags;
1564 script->n_registers = n_registers;
1565 script->n_arguments = n_args;
1566 script->arguments = args;
1567 /* see function-scope tests */
1568 if (cx->version > 5) {
1569 /* FIXME: or original target? */
1570 fun = swfdec_as_script_function_new (frame->scope ? frame->scope : SWFDEC_AS_SCOPE (frame), frame->target, script);
1571 } else {
1572 SwfdecAsScope *scope = frame->scope ? frame->scope : SWFDEC_AS_SCOPE (frame);
1573 while (scope->next)
1574 scope = scope->next;
1575 fun = swfdec_as_script_function_new (scope, frame->target, script);
1577 if (fun == NULL)
1578 return;
1579 /* attach the function */
1580 if (*function_name == '\0') {
1581 swfdec_as_stack_ensure_free (cx, 1);
1582 SWFDEC_AS_VALUE_SET_OBJECT (swfdec_as_stack_push (cx), SWFDEC_AS_OBJECT (fun));
1583 } else {
1584 SwfdecAsValue funval;
1585 /* FIXME: really varobj? Not eval or sth like that? */
1586 name = swfdec_as_context_get_string (cx, function_name);
1587 SWFDEC_AS_VALUE_SET_OBJECT (&funval, SWFDEC_AS_OBJECT (fun));
1588 swfdec_as_object_set_variable (frame->target, name, &funval);
1591 /* update current context */
1592 frame->pc += 3 + len + size;
1593 g_free (function_name);
1596 static void
1597 swfdec_action_bitwise (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1599 int a, b;
1601 a = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1602 b = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1604 switch (action) {
1605 case 0x60:
1606 a = (int) (a & b);
1607 break;
1608 case 0x61:
1609 a = (int) (a | b);
1610 break;
1611 case 0x62:
1612 a = (int) (a ^ b);
1613 break;
1614 default:
1615 g_assert_not_reached ();
1616 break;
1619 swfdec_as_stack_pop (cx);
1620 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_peek (cx, 1), a);
1623 static void
1624 swfdec_action_shift (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1626 int amount, value;
1628 amount = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1));
1629 amount &= 31;
1630 value = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 2));
1632 switch (action) {
1633 case 0x63:
1634 value = value << amount;
1635 break;
1636 case 0x64:
1637 value = ((gint) value) >> amount;
1638 break;
1639 case 0x65:
1640 value = ((guint) value) >> amount;
1641 break;
1642 default:
1643 g_assert_not_reached ();
1646 swfdec_as_stack_pop (cx);
1647 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_peek (cx, 1), value);
1650 static void
1651 swfdec_action_to_integer (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1653 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
1655 SWFDEC_AS_VALUE_SET_INT (val, swfdec_as_value_to_integer (cx, val));
1658 static void
1659 swfdec_action_target_path (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1661 SwfdecAsValue *val;
1662 SwfdecMovie *movie;
1663 char *s;
1665 val = swfdec_as_stack_peek (cx, 1);
1667 if (!SWFDEC_AS_VALUE_IS_OBJECT (val) ||
1668 !SWFDEC_IS_MOVIE (movie = (SwfdecMovie *) SWFDEC_AS_VALUE_GET_OBJECT (val))) {
1669 SWFDEC_FIXME ("What's the TargetPath for non-movies?");
1670 SWFDEC_AS_VALUE_SET_UNDEFINED (val);
1671 return;
1673 s = swfdec_movie_get_path (movie);
1674 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_give_string (cx, s));
1677 static void
1678 swfdec_action_define_local (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1680 SwfdecAsObject *target;
1681 const char *name;
1683 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
1684 if (cx->frame->is_local) {
1685 target = SWFDEC_AS_OBJECT (cx->frame);
1686 } else {
1687 target = cx->frame->target;
1689 swfdec_as_object_set_variable (target, name,
1690 swfdec_as_stack_peek (cx, 1));
1691 swfdec_as_stack_pop_n (cx, 2);
1694 static void
1695 swfdec_action_define_local2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1697 SwfdecAsValue val = { 0, };
1698 SwfdecAsObject *target;
1699 const char *name;
1701 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1702 if (cx->frame->is_local) {
1703 target = SWFDEC_AS_OBJECT (cx->frame);
1704 } else {
1705 target = cx->frame->target;
1707 swfdec_as_object_set_variable (target, name, &val);
1708 swfdec_as_stack_pop (cx);
1711 static void
1712 swfdec_action_return (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1714 swfdec_as_frame_return (cx->frame, swfdec_as_stack_pop (cx));
1717 static void
1718 swfdec_action_delete (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1720 SwfdecAsValue *val;
1721 const char *name;
1722 gboolean success = FALSE;
1724 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1));
1725 val = swfdec_as_stack_peek (cx, 2);
1726 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
1727 success = swfdec_as_object_delete_variable (
1728 SWFDEC_AS_VALUE_GET_OBJECT (val), name) == SWFDEC_AS_DELETE_DELETED;
1730 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
1731 swfdec_as_stack_pop_n (cx, 1);
1734 static void
1735 swfdec_action_delete2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1737 SwfdecAsValue *val;
1738 const char *name;
1739 gboolean success = FALSE;
1741 val = swfdec_as_stack_peek (cx, 1);
1742 name = swfdec_as_value_to_string (cx, val);
1743 success = swfdec_as_frame_delete_variable (cx->frame, name) == SWFDEC_AS_DELETE_DELETED;
1744 SWFDEC_AS_VALUE_SET_BOOLEAN (val, success);
1747 static void
1748 swfdec_action_store_register (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1750 if (len != 1) {
1751 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
1752 return;
1754 if (!swfdec_action_has_register (cx, *data)) {
1755 SWFDEC_ERROR ("Cannot store into register %u, not enough registers", (guint) *data);
1756 return;
1758 cx->frame->registers[*data] = *swfdec_as_stack_peek (cx, 1);
1761 static void
1762 swfdec_action_modulo (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1764 double x, y;
1766 y = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1));
1767 x = swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 2));
1768 /* yay, we're portable! */
1769 if (y == 0.0) {
1770 x = NAN;
1771 } else {
1772 errno = 0;
1773 x = fmod (x, y);
1774 if (errno != 0) {
1775 SWFDEC_FIXME ("errno set after fmod");
1778 swfdec_as_stack_pop (cx);
1779 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1), x);
1782 static void
1783 swfdec_action_swap (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1785 swfdec_as_stack_swap (cx, 1, 2);
1788 static void
1789 swfdec_action_to_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1791 SWFDEC_AS_VALUE_SET_NUMBER (swfdec_as_stack_peek (cx, 1),
1792 swfdec_as_value_to_number (cx, swfdec_as_stack_peek (cx, 1)));
1795 static void
1796 swfdec_action_to_string (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1798 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_peek (cx, 1),
1799 swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 1)));
1802 static void
1803 swfdec_action_type_of (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1805 SwfdecAsValue *val;
1806 const char *type;
1808 val = swfdec_as_stack_peek (cx, 1);
1809 switch (val->type) {
1810 case SWFDEC_AS_TYPE_NUMBER:
1811 type = SWFDEC_AS_STR_number;
1812 break;
1813 case SWFDEC_AS_TYPE_BOOLEAN:
1814 type = SWFDEC_AS_STR_boolean;
1815 break;
1816 case SWFDEC_AS_TYPE_STRING:
1817 type = SWFDEC_AS_STR_string;
1818 break;
1819 case SWFDEC_AS_TYPE_UNDEFINED:
1820 type = SWFDEC_AS_STR_undefined;
1821 break;
1822 case SWFDEC_AS_TYPE_NULL:
1823 type = SWFDEC_AS_STR_null;
1824 break;
1825 case SWFDEC_AS_TYPE_OBJECT:
1827 SwfdecAsObject *obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
1828 if (SWFDEC_IS_MOVIE (obj)) {
1829 type = SWFDEC_AS_STR_movieclip;
1830 } else if (SWFDEC_IS_AS_FUNCTION (obj)) {
1831 type = SWFDEC_AS_STR_function;
1832 } else {
1833 type = SWFDEC_AS_STR_object;
1836 break;
1837 default:
1838 g_assert_not_reached ();
1839 type = SWFDEC_AS_STR_EMPTY;
1840 break;
1842 SWFDEC_AS_VALUE_SET_STRING (val, type);
1845 static void
1846 swfdec_action_get_time (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1848 GTimeVal tv;
1849 gulong diff;
1851 swfdec_as_context_get_time (cx, &tv);
1852 /* we assume here that swfdec_as_context_get_time always returns a tv > start_time */
1853 diff = tv.tv_sec - cx->start_time.tv_sec;
1854 if (diff > G_MAXULONG / 1000 - 1) {
1855 SWFDEC_ERROR ("FIXME: time overflow");
1857 diff *= 1000;
1858 diff = diff + (tv.tv_usec - cx->start_time.tv_usec) / 1000;
1860 SWFDEC_AS_VALUE_SET_INT (swfdec_as_stack_push (cx), diff);
1863 static void
1864 swfdec_action_extends (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1866 SwfdecAsValue *superclass, *subclass, proto;
1867 SwfdecAsObject *prototype;
1868 SwfdecAsObject *super;
1870 superclass = swfdec_as_stack_peek (cx, 1);
1871 subclass = swfdec_as_stack_peek (cx, 2);
1872 if (!SWFDEC_AS_VALUE_IS_OBJECT (superclass) ||
1873 !SWFDEC_IS_AS_FUNCTION (SWFDEC_AS_VALUE_GET_OBJECT (superclass))) {
1874 SWFDEC_ERROR ("superclass is not a function");
1875 goto fail;
1877 if (!SWFDEC_AS_VALUE_IS_OBJECT (subclass)) {
1878 SWFDEC_ERROR ("subclass is not an object");
1879 goto fail;
1881 super = SWFDEC_AS_VALUE_GET_OBJECT (superclass);
1882 prototype = swfdec_as_object_new_empty (cx);
1883 if (prototype == NULL)
1884 return;
1885 swfdec_as_object_get_variable (super, SWFDEC_AS_STR_prototype, &proto);
1886 swfdec_as_object_set_variable (prototype, SWFDEC_AS_STR___proto__, &proto);
1887 swfdec_as_object_set_variable_and_flags (prototype, SWFDEC_AS_STR___constructor__,
1888 superclass, SWFDEC_AS_VARIABLE_HIDDEN);
1889 SWFDEC_AS_VALUE_SET_OBJECT (&proto, prototype);
1890 swfdec_as_object_set_variable (SWFDEC_AS_VALUE_GET_OBJECT (subclass),
1891 SWFDEC_AS_STR_prototype, &proto);
1892 fail:
1893 swfdec_as_stack_pop_n (cx, 2);
1896 static gboolean
1897 swfdec_action_do_enumerate (SwfdecAsObject *object, const char *variable,
1898 SwfdecAsValue *value, guint flags, gpointer cxp)
1900 SwfdecAsContext *cx = cxp;
1902 if (flags & SWFDEC_AS_VARIABLE_HIDDEN)
1903 return TRUE;
1904 swfdec_as_stack_ensure_free (cx, 1);
1905 SWFDEC_AS_VALUE_SET_STRING (swfdec_as_stack_push (cx), variable);
1906 return TRUE;
1909 static void
1910 swfdec_action_enumerate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1912 SwfdecAsValue *val;
1913 SwfdecAsObject *obj;
1915 val = swfdec_as_stack_peek (cx, 1);
1917 swfdec_as_interpret_eval (cx, NULL, val);
1918 if (!SWFDEC_AS_VALUE_IS_OBJECT (val)) {
1919 SWFDEC_ERROR ("Enumerate not pointing to an object");
1920 SWFDEC_AS_VALUE_SET_NULL (val);
1921 return;
1923 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
1924 SWFDEC_AS_VALUE_SET_NULL (val);
1925 swfdec_as_object_foreach (obj, swfdec_action_do_enumerate, cx);
1928 static void
1929 swfdec_action_enumerate2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1931 SwfdecAsValue *val;
1932 SwfdecAsObject *obj;
1934 val = swfdec_as_stack_peek (cx, 1);
1935 if (!SWFDEC_AS_VALUE_IS_OBJECT (val)) {
1936 SWFDEC_ERROR ("Enumerate2 called without an object");
1937 SWFDEC_AS_VALUE_SET_NULL (val);
1938 return;
1940 obj = SWFDEC_AS_VALUE_GET_OBJECT (val);
1941 SWFDEC_AS_VALUE_SET_NULL (val);
1942 swfdec_as_object_foreach (obj, swfdec_action_do_enumerate, cx);
1945 static void
1946 swfdec_action_logical (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1948 SwfdecAsValue *val;
1949 gboolean l, r;
1951 l = swfdec_as_value_to_boolean (cx, swfdec_as_stack_peek (cx, 1));
1952 val = swfdec_as_stack_peek (cx, 2);
1953 r = swfdec_as_value_to_boolean (cx, val);
1955 SWFDEC_AS_VALUE_SET_BOOLEAN (val, (action == 0x10) ? (l && r) : (l || r));
1956 swfdec_as_stack_pop (cx);
1959 static void
1960 swfdec_action_char_to_ascii_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1962 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
1963 const char *s = swfdec_as_value_to_string (cx, val);
1965 char *ascii;
1966 ascii = g_convert (s, -1, "LATIN1", "UTF-8", NULL, NULL, NULL);
1967 if (ascii == NULL) {
1968 /* This can happen if a Flash 5 movie gets loaded into a Flash 7 movie */
1969 SWFDEC_FIXME ("Someone threw unconvertible text %s at Flash <= 5", s);
1970 SWFDEC_AS_VALUE_SET_INT (val, 0); /* FIXME: what to return??? */
1971 } else {
1972 SWFDEC_AS_VALUE_SET_INT (val, (guchar) ascii[0]);
1973 g_free (ascii);
1977 static void
1978 swfdec_action_char_to_ascii (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1980 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
1981 const char *s = swfdec_as_value_to_string(cx, val);
1982 gunichar *uni;
1984 uni = g_utf8_to_ucs4_fast (s, -1, NULL);
1985 if (uni == NULL) {
1986 /* This should never happen, everything is valid UTF-8 in here */
1987 g_warning ("conversion of character %s failed", s);
1988 SWFDEC_AS_VALUE_SET_INT (val, 0);
1989 } else {
1990 SWFDEC_AS_VALUE_SET_INT (val, uni[0]);
1991 g_free (uni);
1995 static void
1996 swfdec_action_ascii_to_char (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
1998 char *s;
1999 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2000 gunichar c = ((guint) swfdec_as_value_to_integer (cx, val)) % 65536;
2002 s = g_ucs4_to_utf8 (&c, 1, NULL, NULL, NULL);
2003 if (s == NULL) {
2004 g_warning ("conversion of character %u failed", (guint) c);
2005 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2006 } else {
2007 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, s));
2008 g_free (s);
2012 static void
2013 swfdec_action_ascii_to_char_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2015 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2016 char s[2];
2017 char *utf8;
2019 s[0] = ((guint) swfdec_as_value_to_integer (cx, val)) % 256;
2020 s[1] = 0;
2022 utf8 = g_convert (s, -1, "UTF-8", "LATIN1", NULL, NULL, NULL);
2023 if (utf8 == NULL) {
2024 g_warning ("conversion of character %u failed", (guint) s[0]);
2025 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2026 } else {
2027 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, utf8));
2028 g_free (utf8);
2032 static void
2033 swfdec_action_mb_ascii_to_char_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2035 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2036 char s[3];
2037 char *utf8;
2038 guint i;
2040 i = ((guint) swfdec_as_value_to_integer (cx, val));
2041 if (i > 255) {
2042 s[0] = i / 256;
2043 s[1] = i % 256;
2044 s[2] = 0;
2045 } else {
2046 s[0] = i;
2047 s[1] = 0;
2049 utf8 = g_convert (s, -1, "UTF-8", "LATIN1", NULL, NULL, NULL);
2050 if (utf8 == NULL) {
2051 g_warning ("conversion of character %u failed", i);
2052 SWFDEC_AS_VALUE_SET_STRING (val, SWFDEC_AS_STR_EMPTY);
2053 } else {
2054 SWFDEC_AS_VALUE_SET_STRING (val, swfdec_as_context_get_string (cx, utf8));
2055 g_free (utf8);
2059 static void
2060 swfdec_action_with (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2062 SwfdecAsObject *object;
2063 guint offset;
2065 if (len != 2) {
2066 SWFDEC_ERROR ("With action requires a length of 2, but got %u", len);
2067 swfdec_as_stack_pop (cx);
2068 return;
2070 offset = data[0] | (data[1] << 8);
2071 object = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 1));
2072 if (object == NULL) {
2073 SWFDEC_INFO ("With called without an object, skipping");
2074 cx->frame->pc = (guint8 *) data + len + offset;
2075 } else {
2076 swfdec_as_with_new (object, data + len, offset);
2078 swfdec_as_stack_pop (cx);
2081 static void
2082 swfdec_action_remove_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2084 SwfdecAsValue *val = swfdec_as_stack_peek (cx, 1);
2085 SwfdecAsObject *sprite;
2086 SwfdecMovie *movie;
2088 if (SWFDEC_AS_VALUE_IS_STRING (val)) {
2089 const char *name = SWFDEC_AS_VALUE_GET_STRING (val);
2091 swfdec_as_context_eval (cx, NULL, name, val);
2093 if (SWFDEC_AS_VALUE_IS_OBJECT (val)) {
2094 sprite = SWFDEC_AS_VALUE_GET_OBJECT (val);
2095 } else {
2096 SWFDEC_FIXME ("unknown type in RemoveSprite");
2097 goto fail;
2099 if (!SWFDEC_IS_MOVIE (sprite)) {
2100 SWFDEC_FIXME ("cannot remove non movieclip objects");
2101 goto fail;
2103 movie = SWFDEC_MOVIE (sprite);
2104 if (swfdec_depth_classify (movie->depth) == SWFDEC_DEPTH_CLASS_DYNAMIC) {
2105 SWFDEC_LOG ("removing clip %s", movie->name);
2106 swfdec_movie_remove (movie);
2108 fail:
2109 swfdec_as_stack_pop (cx);
2112 static void
2113 swfdec_action_clone_sprite (SwfdecAsContext *cx, guint action, const guint8 *data, guint len)
2115 SwfdecMovie *movie, *new_movie;
2116 SwfdecAsObject *obj;
2117 const char *new_name;
2118 int depth;
2120 depth = swfdec_as_value_to_integer (cx, swfdec_as_stack_peek (cx, 1)) - 16384;
2121 new_name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 2));
2122 if (SWFDEC_AS_VALUE_IS_STRING (swfdec_as_stack_peek (cx, 3))) {
2123 const char *name;
2124 name = swfdec_as_value_to_string (cx, swfdec_as_stack_peek (cx, 3));
2125 swfdec_as_context_eval (cx, NULL, name, swfdec_as_stack_peek (cx, 3));
2127 obj = swfdec_as_value_to_object (cx, swfdec_as_stack_peek (cx, 3));
2128 if (!SWFDEC_IS_MOVIE(obj)) {
2129 SWFDEC_ERROR ("Object is not an SwfdecMovie object");
2130 swfdec_as_stack_pop_n (cx, 3);
2131 return;
2133 movie = SWFDEC_MOVIE(obj);
2134 new_movie = swfdec_movie_duplicate (movie, new_name, depth);
2135 if (new_movie) {
2136 SWFDEC_LOG ("duplicated %s as %s to depth %u", movie->name, new_movie->name, new_movie->depth);
2137 if (SWFDEC_IS_SPRITE_MOVIE (new_movie)) {
2138 g_queue_push_tail (SWFDEC_PLAYER (cx)->init_queue, new_movie);
2139 swfdec_movie_queue_script (new_movie, SWFDEC_EVENT_LOAD);
2140 swfdec_movie_run_construct (new_movie);
2142 swfdec_movie_initialize (new_movie);
2144 swfdec_as_stack_pop_n (cx, 3);
2147 /*** PRINT FUNCTIONS ***/
2149 static char *
2150 swfdec_action_print_with (guint action, const guint8 *data, guint len)
2152 if (len != 2) {
2153 SWFDEC_ERROR ("With action requires a length of 2, but got %u", len);
2154 return NULL;
2156 return g_strdup_printf ("With %u", data[0] | (data[1] << 8));
2159 static char *
2160 swfdec_action_print_store_register (guint action, const guint8 *data, guint len)
2162 if (len != 1) {
2163 SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len);
2164 return NULL;
2166 return g_strdup_printf ("StoreRegister %u", (guint) *data);
2169 static char *
2170 swfdec_action_print_set_target (guint action, const guint8 *data, guint len)
2172 if (!memchr (data, 0, len)) {
2173 SWFDEC_ERROR ("SetTarget action does not specify a string");
2174 return NULL;
2176 return g_strconcat ("SetTarget ", data, NULL);
2179 static char *
2180 swfdec_action_print_define_function (guint action, const guint8 *data, guint len)
2182 SwfdecBits bits;
2183 GString *string;
2184 const char *function_name;
2185 guint i, n_args, size;
2186 gboolean v2 = (action == 0x8e);
2188 string = g_string_new (v2 ? "DefineFunction2 " : "DefineFunction ");
2189 swfdec_bits_init_data (&bits, data, len);
2190 function_name = swfdec_bits_get_string (&bits);
2191 if (function_name == NULL) {
2192 SWFDEC_ERROR ("could not parse function name");
2193 g_string_free (string, TRUE);
2194 return NULL;
2196 if (*function_name) {
2197 g_string_append (string, function_name);
2198 g_string_append_c (string, ' ');
2200 n_args = swfdec_bits_get_u16 (&bits);
2201 g_string_append_c (string, '(');
2202 if (v2) {
2203 /* n_regs = */ swfdec_bits_get_u8 (&bits);
2204 /* flags = */ swfdec_bits_get_u16 (&bits);
2207 for (i = 0; i < n_args; i++) {
2208 guint preload;
2209 const char *arg_name;
2210 if (v2)
2211 preload = swfdec_bits_get_u8 (&bits);
2212 else
2213 preload = 0;
2214 arg_name = swfdec_bits_get_string (&bits);
2215 if (preload == 0 && (arg_name == NULL || *arg_name == '\0')) {
2216 SWFDEC_ERROR ("empty argument name not allowed");
2217 g_string_free (string, TRUE);
2218 return NULL;
2220 if (i)
2221 g_string_append (string, ", ");
2222 g_string_append (string, arg_name);
2223 if (preload)
2224 g_string_append_printf (string, " (%u)", preload);
2226 g_string_append_c (string, ')');
2227 size = swfdec_bits_get_u16 (&bits);
2228 g_string_append_printf (string, " %u", size);
2229 return g_string_free (string, FALSE);
2232 static char *
2233 swfdec_action_print_get_url2 (guint action, const guint8 *data, guint len)
2235 guint method;
2237 if (len != 1) {
2238 SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len);
2239 return NULL;
2241 method = data[0] >> 6;
2242 if (method == 3) {
2243 SWFDEC_ERROR ("GetURL method 3 invalid");
2244 method = 0;
2246 if (method) {
2247 SWFDEC_FIXME ("implement encoding variables using %s", method == 1 ? "GET" : "POST");
2249 return g_strdup_printf ("GetURL2%s%s%s", method == 0 ? "" : (method == 1 ? " GET" : " POST"),
2250 data[0] & 2 ? " LoadTarget" : "", data[0] & 1 ? " LoadVariables" : "");
2253 static char *
2254 swfdec_action_print_get_url (guint action, const guint8 *data, guint len)
2256 SwfdecBits bits;
2257 char *url, *target, *ret;
2259 swfdec_bits_init_data (&bits, data, len);
2260 url = swfdec_bits_get_string (&bits);
2261 target = swfdec_bits_get_string (&bits);
2262 if (url == NULL) {
2263 SWFDEC_ERROR ("not enough data in GetURL");
2264 url = g_strdup ("???");
2266 if (target == NULL) {
2267 SWFDEC_ERROR ("not enough data in GetURL");
2268 target = g_strdup ("???");
2270 if (swfdec_bits_left (&bits)) {
2271 SWFDEC_WARNING ("leftover bytes in GetURL action");
2273 ret = g_strdup_printf ("GetURL %s %s", url, target);
2274 g_free (url);
2275 g_free (target);
2276 return ret;
2279 static char *
2280 swfdec_action_print_if (guint action, const guint8 *data, guint len)
2282 if (len != 2) {
2283 SWFDEC_ERROR ("If action length invalid (is %u, should be 2", len);
2284 return NULL;
2286 return g_strdup_printf ("If %d", GINT16_FROM_LE (*((gint16*) data)));
2289 static char *
2290 swfdec_action_print_jump (guint action, const guint8 *data, guint len)
2292 if (len != 2) {
2293 SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len);
2294 return NULL;
2296 return g_strdup_printf ("Jump %d", GINT16_FROM_LE (*((gint16*) data)));
2299 static char *
2300 swfdec_action_print_push (guint action, const guint8 *data, guint len)
2302 gboolean first = TRUE;
2303 SwfdecBits bits;
2304 GString *string = g_string_new ("Push");
2306 swfdec_bits_init_data (&bits, data, len);
2307 while (swfdec_bits_left (&bits)) {
2308 guint type = swfdec_bits_get_u8 (&bits);
2309 if (first)
2310 g_string_append (string, " ");
2311 else
2312 g_string_append (string, ", ");
2313 first = FALSE;
2314 switch (type) {
2315 case 0: /* string */
2317 /* FIXME: need version! */
2318 char *s = swfdec_bits_get_string (&bits);
2319 if (!s) {
2320 g_string_free (string, TRUE);
2321 return NULL;
2323 g_string_append_c (string, '"');
2324 g_string_append (string, s);
2325 g_string_append_c (string, '"');
2326 g_free (s);
2327 break;
2329 case 1: /* float */
2330 g_string_append_printf (string, "%g", swfdec_bits_get_float (&bits));
2331 break;
2332 case 2: /* null */
2333 g_string_append (string, "null");
2334 break;
2335 case 3: /* undefined */
2336 g_string_append (string, "void");
2337 break;
2338 case 4: /* register */
2339 g_string_append_printf (string, "Register %u", swfdec_bits_get_u8 (&bits));
2340 break;
2341 case 5: /* boolean */
2342 g_string_append (string, swfdec_bits_get_u8 (&bits) ? "True" : "False");
2343 break;
2344 case 6: /* double */
2345 g_string_append_printf (string, "%g", swfdec_bits_get_double (&bits));
2346 break;
2347 case 7: /* 32bit int */
2348 g_string_append_printf (string, "%d", swfdec_bits_get_u32 (&bits));
2349 break;
2350 case 8: /* 8bit ConstantPool address */
2351 g_string_append_printf (string, "Pool %u", swfdec_bits_get_u8 (&bits));
2352 break;
2353 case 9: /* 16bit ConstantPool address */
2354 g_string_append_printf (string, "Pool %u", swfdec_bits_get_u16 (&bits));
2355 break;
2356 default:
2357 SWFDEC_ERROR ("Push: type %u not implemented", type);
2358 return NULL;
2361 return g_string_free (string, FALSE);
2364 /* NB: constant pool actions are special in that they are called at init time */
2365 static char *
2366 swfdec_action_print_constant_pool (guint action, const guint8 *data, guint len)
2368 guint i;
2369 GString *string;
2370 SwfdecConstantPool *pool;
2372 /* FIXME: version */
2373 pool = swfdec_constant_pool_new_from_action (data, len, 6);
2374 if (pool == NULL)
2375 return g_strdup ("ConstantPool (invalid)");
2376 string = g_string_new ("ConstantPool");
2377 for (i = 0; i < swfdec_constant_pool_size (pool); i++) {
2378 g_string_append (string, i ? ", " : " ");
2379 g_string_append (string, swfdec_constant_pool_get (pool, i));
2380 g_string_append_printf (string, " (%u)", i);
2382 return g_string_free (string, FALSE);
2385 #if 0
2386 static char *
2387 swfdec_action_print_wait_for_frame2 (guint action, const guint8 *data, guint len)
2389 if (len != 1) {
2390 SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data");
2391 return NULL;
2393 return g_strdup_printf ("WaitForFrame2 %u", (guint) *data);
2395 #endif
2397 static char *
2398 swfdec_action_print_goto_frame2 (guint action, const guint8 *data, guint len)
2400 gboolean play, bias;
2401 SwfdecBits bits;
2403 swfdec_bits_init_data (&bits, data, len);
2404 if (swfdec_bits_getbits (&bits, 6)) {
2405 SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0");
2407 bias = swfdec_bits_getbit (&bits);
2408 play = swfdec_bits_getbit (&bits);
2409 if (bias) {
2410 return g_strdup_printf ("GotoFrame2 %s +%u", play ? "play" : "stop",
2411 swfdec_bits_get_u16 (&bits));
2412 } else {
2413 return g_strdup_printf ("GotoFrame2 %s", play ? "play" : "stop");
2417 static char *
2418 swfdec_action_print_goto_frame (guint action, const guint8 *data, guint len)
2420 guint frame;
2422 if (len != 2)
2423 return NULL;
2425 frame = data[0] | (data[1] << 8);
2426 return g_strdup_printf ("GotoFrame %u", frame);
2429 static char *
2430 swfdec_action_print_goto_label (guint action, const guint8 *data, guint len)
2432 if (!memchr (data, 0, len)) {
2433 SWFDEC_ERROR ("GotoLabel action does not specify a string");
2434 return NULL;
2437 return g_strdup_printf ("GotoLabel %s", data);
2440 static char *
2441 swfdec_action_print_wait_for_frame (guint action, const guint8 *data, guint len)
2443 guint frame, jump;
2445 if (len != 3)
2446 return NULL;
2448 frame = data[0] | (data[1] << 8);
2449 jump = data[2];
2450 return g_strdup_printf ("WaitForFrame %u %u", frame, jump);
2453 /*** BIG FUNCTION TABLE ***/
2455 const SwfdecActionSpec swfdec_as_actions[256] = {
2456 /* version 3 */
2457 [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 } },
2458 [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 } },
2459 [SWFDEC_AS_ACTION_PLAY] = { "Play", NULL, 0, 0, { swfdec_action_play, swfdec_action_play, swfdec_action_play, swfdec_action_play, swfdec_action_play } },
2460 [SWFDEC_AS_ACTION_STOP] = { "Stop", NULL, 0, 0, { swfdec_action_stop, swfdec_action_stop, swfdec_action_stop, swfdec_action_stop, swfdec_action_stop } },
2461 [SWFDEC_AS_ACTION_TOGGLE_QUALITY] = { "ToggleQuality", NULL },
2462 [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 } },
2463 /* version 4 */
2464 [SWFDEC_AS_ACTION_ADD] = { "Add", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } },
2465 [SWFDEC_AS_ACTION_SUBTRACT] = { "Subtract", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } },
2466 [SWFDEC_AS_ACTION_MULTIPLY] = { "Multiply", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } },
2467 [SWFDEC_AS_ACTION_DIVIDE] = { "Divide", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } },
2468 [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 } },
2469 [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 } },
2470 [SWFDEC_AS_ACTION_AND] = { "And", NULL, 2, 1, { NULL, /* FIXME */NULL, swfdec_action_logical, swfdec_action_logical, swfdec_action_logical } },
2471 [SWFDEC_AS_ACTION_OR] = { "Or", NULL, 2, 1, { NULL, /* FIXME */NULL, swfdec_action_logical, swfdec_action_logical, swfdec_action_logical } },
2472 [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 } },
2473 [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 } },
2474 [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 } },
2475 [SWFDEC_AS_ACTION_STRING_EXTRACT] = { "StringExtract", NULL },
2476 [SWFDEC_AS_ACTION_POP] = { "Pop", NULL, 1, 0, { NULL, swfdec_action_pop, swfdec_action_pop, swfdec_action_pop, swfdec_action_pop } },
2477 [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 } },
2478 [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 } },
2479 [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 } },
2480 [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 } },
2481 [0x21] = { "StringAdd", NULL, 2, 1, { NULL, swfdec_action_string_add, swfdec_action_string_add, swfdec_action_string_add, swfdec_action_string_add } },
2482 [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 } },
2483 [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 } },
2484 [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 } },
2485 [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 } },
2486 [SWFDEC_AS_ACTION_TRACE] = { "Trace", NULL, 1, 0, { NULL, swfdec_action_trace, swfdec_action_trace, swfdec_action_trace, swfdec_action_trace } },
2487 [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 } },
2488 [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 } },
2489 [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 } },
2490 /* version 7 */
2491 [SWFDEC_AS_ACTION_THROW] = { "Throw", NULL },
2492 [SWFDEC_AS_ACTION_CAST] = { "Cast", NULL },
2493 [SWFDEC_AS_ACTION_IMPLEMENTS] = { "Implements", NULL },
2494 /* version 4 */
2495 [0x30] = { "RandomNumber", NULL, 1, 1, { NULL, swfdec_action_random_number, swfdec_action_random_number, swfdec_action_random_number, swfdec_action_random_number } },
2496 [SWFDEC_AS_ACTION_MB_STRING_LENGTH] = { "MBStringLength", NULL },
2497 [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 } },
2498 [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 } },
2499 [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 } },
2500 [SWFDEC_AS_ACTION_MB_STRING_EXTRACT] = { "MBStringExtract", NULL },
2501 [SWFDEC_AS_ACTION_MB_CHAR_TO_ASCII] = { "MBCharToAscii", NULL },
2502 [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 } },
2503 /* version 5 */
2504 [SWFDEC_AS_ACTION_DELETE] = { "Delete", NULL, 2, 1, { NULL, NULL, swfdec_action_delete, swfdec_action_delete, swfdec_action_delete } },
2505 [SWFDEC_AS_ACTION_DELETE2] = { "Delete2", NULL, 1, 1, { NULL, NULL, swfdec_action_delete2, swfdec_action_delete2, swfdec_action_delete2 } },
2506 [SWFDEC_AS_ACTION_DEFINE_LOCAL] = { "DefineLocal", NULL, 2, 0, { NULL, NULL, swfdec_action_define_local, swfdec_action_define_local, swfdec_action_define_local } },
2507 [SWFDEC_AS_ACTION_CALL_FUNCTION] = { "CallFunction", NULL, -1, 1, { NULL, NULL, swfdec_action_call_function, swfdec_action_call_function, swfdec_action_call_function } },
2508 [SWFDEC_AS_ACTION_RETURN] = { "Return", NULL, 1, 0, { NULL, NULL, swfdec_action_return, swfdec_action_return, swfdec_action_return } },
2509 [SWFDEC_AS_ACTION_MODULO] = { "Modulo", NULL, 2, 1, { NULL, NULL, swfdec_action_modulo, swfdec_action_modulo, swfdec_action_modulo } },
2510 [SWFDEC_AS_ACTION_NEW_OBJECT] = { "NewObject", NULL, -1, 1, { NULL, NULL, swfdec_action_new_object, swfdec_action_new_object, swfdec_action_new_object } },
2511 [SWFDEC_AS_ACTION_DEFINE_LOCAL2] = { "DefineLocal2", NULL, 1, 0, { NULL, NULL, swfdec_action_define_local2, swfdec_action_define_local2, swfdec_action_define_local2 } },
2512 [SWFDEC_AS_ACTION_INIT_ARRAY] = { "InitArray", NULL, -1, 1, { NULL, NULL, swfdec_action_init_array, swfdec_action_init_array, swfdec_action_init_array } },
2513 [SWFDEC_AS_ACTION_INIT_OBJECT] = { "InitObject", NULL, -1, 1, { NULL, NULL, swfdec_action_init_object, swfdec_action_init_object, swfdec_action_init_object } },
2514 [SWFDEC_AS_ACTION_TYPE_OF] = { "TypeOf", NULL, 1, 1, { NULL, NULL, swfdec_action_type_of, swfdec_action_type_of, swfdec_action_type_of } },
2515 [SWFDEC_AS_ACTION_TARGET_PATH] = { "TargetPath", NULL, 1, 1, { NULL, NULL, swfdec_action_target_path, swfdec_action_target_path, swfdec_action_target_path } },
2516 [SWFDEC_AS_ACTION_ENUMERATE] = { "Enumerate", NULL, 1, -1, { NULL, NULL, swfdec_action_enumerate, swfdec_action_enumerate, swfdec_action_enumerate } },
2517 [SWFDEC_AS_ACTION_ADD2] = { "Add2", NULL, 2, 1, { NULL, NULL, swfdec_action_add2, swfdec_action_add2, swfdec_action_add2 } },
2518 [SWFDEC_AS_ACTION_LESS2] = { "Less2", NULL, 2, 1, { NULL, NULL, swfdec_action_new_comparison, swfdec_action_new_comparison, swfdec_action_new_comparison } },
2519 [SWFDEC_AS_ACTION_EQUALS2] = { "Equals2", NULL, 2, 1, { NULL, NULL, swfdec_action_equals2_5, swfdec_action_equals2, swfdec_action_equals2 } },
2520 [SWFDEC_AS_ACTION_TO_NUMBER] = { "ToNumber", NULL, 1, 1, { NULL, NULL, swfdec_action_to_number, swfdec_action_to_number, swfdec_action_to_number } },
2521 [SWFDEC_AS_ACTION_TO_STRING] = { "ToString", NULL, 1, 1, { NULL, NULL, swfdec_action_to_string, swfdec_action_to_string, swfdec_action_to_string } },
2522 [SWFDEC_AS_ACTION_PUSH_DUPLICATE] = { "PushDuplicate", NULL, 1, 2, { NULL, NULL, swfdec_action_push_duplicate, swfdec_action_push_duplicate, swfdec_action_push_duplicate } },
2523 [SWFDEC_AS_ACTION_SWAP] = { "Swap", NULL, 2, 2, { NULL, NULL, swfdec_action_swap, swfdec_action_swap, swfdec_action_swap } },
2524 [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 } },
2525 [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 } },
2526 [SWFDEC_AS_ACTION_INCREMENT] = { "Increment", NULL, 1, 1, { NULL, NULL, swfdec_action_increment, swfdec_action_increment, swfdec_action_increment } },
2527 [SWFDEC_AS_ACTION_DECREMENT] = { "Decrement", NULL, 1, 1, { NULL, NULL, swfdec_action_decrement, swfdec_action_decrement, swfdec_action_decrement } },
2528 [SWFDEC_AS_ACTION_CALL_METHOD] = { "CallMethod", NULL, -1, 1, { NULL, NULL, swfdec_action_call_method, swfdec_action_call_method, swfdec_action_call_method } },
2529 [SWFDEC_AS_ACTION_NEW_METHOD] = { "NewMethod", NULL, -1, 1, { NULL, NULL, swfdec_action_new_method, swfdec_action_new_method, swfdec_action_new_method } },
2530 /* version 6 */
2531 [SWFDEC_AS_ACTION_INSTANCE_OF] = { "InstanceOf", NULL },
2532 [SWFDEC_AS_ACTION_ENUMERATE2] = { "Enumerate2", NULL, 1, -1, { NULL, NULL, NULL, swfdec_action_enumerate2, swfdec_action_enumerate2 } },
2533 /* version 5 */
2534 [SWFDEC_AS_ACTION_BIT_AND] = { "BitAnd", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } },
2535 [SWFDEC_AS_ACTION_BIT_OR] = { "BitOr", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } },
2536 [SWFDEC_AS_ACTION_BIT_XOR] = { "BitXor", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } },
2537 [SWFDEC_AS_ACTION_BIT_LSHIFT] = { "BitLShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } },
2538 [SWFDEC_AS_ACTION_BIT_RSHIFT] = { "BitRShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } },
2539 [SWFDEC_AS_ACTION_BIT_URSHIFT] = { "BitURShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } },
2540 /* version 6 */
2541 [SWFDEC_AS_ACTION_STRICT_EQUALS] = { "StrictEquals", NULL, 2, 1, { NULL, NULL, NULL, swfdec_action_strict_equals, swfdec_action_strict_equals } },
2542 [SWFDEC_AS_ACTION_GREATER] = { "Greater", NULL, 2, 1, { NULL, NULL, NULL, swfdec_action_new_comparison, swfdec_action_new_comparison } },
2543 [SWFDEC_AS_ACTION_STRING_GREATER] = { "StringGreater", NULL },
2544 /* version 7 */
2545 [SWFDEC_AS_ACTION_EXTENDS] = { "Extends", NULL, 2, 0, { NULL, NULL, NULL, swfdec_action_extends, swfdec_action_extends } },
2546 /* version 3 */
2547 [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 } },
2548 [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 } },
2549 /* version 5 */
2550 [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 } },
2551 [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 } },
2552 /* version 3 */
2553 [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 } },
2554 [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 } },
2555 [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 } },
2556 #if 0
2557 /* version 4 */
2558 [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 } },
2559 #endif
2560 /* version 7 */
2561 [SWFDEC_AS_ACTION_DEFINE_FUNCTION2] = { "DefineFunction2", swfdec_action_print_define_function, 0, -1, { NULL, NULL, NULL, swfdec_action_define_function, swfdec_action_define_function } },
2562 [SWFDEC_AS_ACTION_TRY] = { "Try", NULL },
2563 /* version 5 */
2564 [SWFDEC_AS_ACTION_WITH] = { "With", swfdec_action_print_with, 1, 0, { NULL, NULL, swfdec_action_with, swfdec_action_with, swfdec_action_with } },
2565 /* version 4 */
2566 [SWFDEC_AS_ACTION_PUSH] = { "Push", swfdec_action_print_push, 0, -1, { NULL, swfdec_action_push, swfdec_action_push, swfdec_action_push, swfdec_action_push } },
2567 [SWFDEC_AS_ACTION_JUMP] = { "Jump", swfdec_action_print_jump, 0, 0, { NULL, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump } },
2568 [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 } },
2569 /* version 5 */
2570 [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 } },
2571 /* version 4 */
2572 [SWFDEC_AS_ACTION_IF] = { "If", swfdec_action_print_if, 1, 0, { NULL, swfdec_action_if, swfdec_action_if, swfdec_action_if, swfdec_action_if } },
2573 [SWFDEC_AS_ACTION_CALL] = { "Call", NULL },
2574 [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 } }