Benjamin Otte
2007-Apr-04 10:15 UTC
[Swfdec] Branch 'as' - 4 commits - libswfdec/Makefile.am libswfdec/swfdec_as_context.c libswfdec/swfdec_as_frame.c libswfdec/swfdec_as_interpret.c libswfdec/swfdec_as_interpret.h libswfdec/swfdec_as_object.c libswfdec/swfdec_as_types.c libswfdec/swfdec_as_types.h libswfdec/swfdec_script.c libswfdec/swfdec_script.h
libswfdec/Makefile.am | 2 libswfdec/swfdec_as_context.c | 30 libswfdec/swfdec_as_frame.c | 4 libswfdec/swfdec_as_interpret.c | 2472 +++++++++++++++++++++++++++++++++++ libswfdec/swfdec_as_interpret.h | 45 libswfdec/swfdec_as_object.c | 5 libswfdec/swfdec_as_types.c | 43 libswfdec/swfdec_as_types.h | 5 libswfdec/swfdec_script.c | 2789 ---------------------------------------- libswfdec/swfdec_script.h | 4 10 files changed, 2592 insertions(+), 2807 deletions(-) New commits: diff-tree 121a4494b3f5c785daac627b908593585b591182 (from ee1c84df7a50b023262001feb21c90380798161a) Author: Benjamin Otte <otte@gnome.org> Date: Wed Apr 4 14:48:41 2007 +0200 put interpreting in its own file and implement the first few functions The ultra-important first ever working ActionScript movie works again diff --git a/libswfdec/Makefile.am b/libswfdec/Makefile.am index b46bcc0..51ebd32 100644 --- a/libswfdec/Makefile.am +++ b/libswfdec/Makefile.am @@ -28,6 +28,7 @@ libswfdec_@SWFDEC_MAJORMINOR@_la_SOURCES swfdec_as_context.c \ swfdec_as_frame.c \ swfdec_as_function.c \ + swfdec_as_interpret.c \ swfdec_as_object.c \ swfdec_as_stack.c \ swfdec_as_types.c \ @@ -114,6 +115,7 @@ noinst_HEADERS = \ swfdec_as_context.h \ swfdec_as_frame.h \ swfdec_as_function.h \ + swfdec_as_interpret.h \ swfdec_as_object.h \ swfdec_as_stack.h \ swfdec_as_types.h \ diff --git a/libswfdec/swfdec_as_context.c b/libswfdec/swfdec_as_context.c index 8fe289a..8d31c73 100644 --- a/libswfdec/swfdec_as_context.c +++ b/libswfdec/swfdec_as_context.c @@ -24,6 +24,7 @@ #include <string.h> #include "swfdec_as_context.h" #include "swfdec_as_frame.h" +#include "swfdec_as_interpret.h" #include "swfdec_as_object.h" #include "swfdec_as_stack.h" #include "swfdec_as_types.h" @@ -327,22 +328,6 @@ swfdec_as_context_new (void) return g_object_new (SWFDEC_TYPE_AS_CONTEXT, NULL); } -/* defines minimum and maximum versions for which we have seperate scripts */ -#define MINSCRIPTVERSION 3 -#define MAXSCRIPTVERSION 7 -#define EXTRACT_VERSION(v) MIN ((v) - MINSCRIPTVERSION, MAXSCRIPTVERSION - MINSCRIPTVERSION) - -typedef void (* SwfdecActionExec) (SwfdecAsContext *cx, guint action, const guint8 *data, guint len); -typedef struct { - const char * name; /* name identifying the action */ - char * (* print) (guint action, const guint8 *data, guint len); - int remove; /* values removed from stack or -1 for dynamic */ - int add; /* values added to the stack or -1 for dynamic */ - SwfdecActionExec exec[MAXSCRIPTVERSION - MINSCRIPTVERSION + 1]; - /* array is for version 3, 4, 5, 6, 7+ */ -} SwfdecActionSpec; - -extern const SwfdecActionSpec actions[256]; void swfdec_as_context_run (SwfdecAsContext *context) { @@ -367,9 +352,11 @@ start: return; script = frame->script; stack = frame->stack; - version = EXTRACT_VERSION (script->version); + version = SWFDEC_AS_EXTRACT_SCRIPT_VERSION (script->version); + context->version = script->version; startpc = script->buffer->data; endpc = startpc + script->buffer->length; + pc = frame->pc; while (TRUE) { if (pc == endpc) { @@ -383,7 +370,7 @@ start: /* decode next action */ action = *pc; - spec = actions + action; + spec = swfdec_as_actions + action; if (action == 0) break; if (action & 0x80) { @@ -405,8 +392,9 @@ start: } /* check action is valid */ if (spec->exec[version] == NULL) { - SWFDEC_ERROR ("cannot interpret action %u %s for version %u", action, - spec->name ? spec->name : "Unknown", script->version); + SWFDEC_ERROR ("cannot interpret action %3u 0x%02X %s for version %u", action, + action, spec->name ? spec->name : "Unknown", script->version); + /* FIXME: figure out what flash player does here */ goto error; } if (spec->remove > 0) { @@ -422,7 +410,7 @@ start: #ifndef G_DISABLE_ASSERT check = (spec->add >= 0 && spec->remove >= 0) ? stack->cur + spec->add - spec->remove : NULL; #endif - spec->exec[version] (NULL, action, data, len); + spec->exec[version] (context, action, data, len); if (frame == context->frame) { #ifndef G_DISABLE_ASSERT if (check != NULL && check != stack->cur) { diff --git a/libswfdec/swfdec_as_interpret.c b/libswfdec/swfdec_as_interpret.c new file mode 100644 index 0000000..6570e65 --- /dev/null +++ b/libswfdec/swfdec_as_interpret.c @@ -0,0 +1,2472 @@ +/* Swfdec + * Copyright (C) 2007 Benjamin Otte <otte@gnome.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "swfdec_as_interpret.h" +#include "swfdec_as_context.h" +#include "swfdec_as_frame.h" +#include "swfdec_debug.h" + +#include <errno.h> +#include <math.h> +#include <string.h> +#include "swfdec_decoder.h" +#include "swfdec_movie.h" +#include "swfdec_player_internal.h" +#include "swfdec_root_movie.h" +#include "swfdec_sprite.h" +#include "swfdec_sprite_movie.h" + +/* Define this to get SWFDEC_WARN'd about missing properties of objects. + * This can be useful to find out about unimplemented native properties, + * but usually just causes a lot of spam. */ +//#define SWFDEC_WARN_MISSING_PROPERTIES + +/*** SUPPORT FUNCTIONS ***/ + +#define swfdec_action_has_register(cx, i) \ + ((i) < ((SwfdecScript *) (cx)->fp->swf)->n_registers) + +static SwfdecMovie * +swfdec_action_get_target (SwfdecAsContext *context) +{ + SwfdecAsObject *object = context->frame->scope; + + if (!SWFDEC_IS_MOVIE (object)) { + SWFDEC_ERROR ("no valid target"); + return NULL; + } + return SWFDEC_MOVIE (object); +} + +#if 0 +static void +swfdec_action_push_string (SwfdecAsContext *cx, const char *s) +{ + JSString *string = JS_NewStringCopyZ (cx, s); + if (string == NULL) + return JS_FALSE; + *cx->fp->sp++ = STRING_TO_JSVAL (string); + return JS_TRUE; +} + +static void +swfdec_value_to_boolean_5 (SwfdecAsContext *cx, jsval val) +{ + if (JSVAL_IS_BOOLEAN (val)) { + return JSVAL_TO_BOOLEAN (val); + } else if (JSVAL_IS_INT (val)) { + return JSVAL_TO_INT (val) != 0; + } else if (JSVAL_IS_DOUBLE (val)) { + double d = *JSVAL_TO_DOUBLE (val); + return d != 0.0 && !isnan (d); + } else if (JSVAL_IS_STRING (val)) { + double d; + if (!JS_ValueToNumber (cx, val, &d)) + return 0; + return d != 0.0 && !isnan (d); + } else if (JSVAL_IS_NULL (val)) { + return JS_FALSE; + } else if (JSVAL_IS_VOID (val)) { + return JS_FALSE; + } else if (JSVAL_IS_OBJECT (val)) { + return JS_TRUE; + } + g_assert_not_reached (); + return JS_FALSE; +} + +static void +swfdec_value_to_boolean_7 (SwfdecAsContext *cx, jsval val) +{ + if (JSVAL_IS_BOOLEAN (val)) { + return JSVAL_TO_BOOLEAN (val); + } else if (JSVAL_IS_INT (val)) { + return JSVAL_TO_INT (val) != 0; + } else if (JSVAL_IS_DOUBLE (val)) { + double d = *JSVAL_TO_DOUBLE (val); + return d != 0.0 && !isnan (d); + } else if (JSVAL_IS_STRING (val)) { + return JS_GetStringLength (JSVAL_TO_STRING (val)) > 0; + } else if (JSVAL_IS_NULL (val)) { + return JS_FALSE; + } else if (JSVAL_IS_VOID (val)) { + return JS_FALSE; + } else if (JSVAL_IS_OBJECT (val)) { + return JS_TRUE; + } + g_assert_not_reached (); + return JS_FALSE; +} + +static double +swfdec_value_to_number (SwfdecAsContext *cx, jsval val) +{ + if (JSVAL_IS_INT (val)) { + return JSVAL_TO_INT (val); + } else if (JSVAL_IS_DOUBLE (val)) { + return *JSVAL_TO_DOUBLE (val); + } else if (JSVAL_IS_BOOLEAN (val)) { + return JSVAL_TO_BOOLEAN (val); + } else if (JSVAL_IS_STRING (val)) { + double d; + if (!JS_ValueToNumber (cx, val, &d)) + return 0; + return d; + } else if (JSVAL_IS_OBJECT(val) && (((SwfdecScript *) cx->fp->swf)->version >= 6)) { + /* Checking for version 6 is completely wrong, but a lot of the testsuite + * depends on it (oops). + * The code calls the valueOf function and returns 0 if no such function exists. + */ + return JSVAL_IS_NULL (val) ? 0 : *cx->runtime->jsNaN; + } else { + return 0; + } +} + +static void +swfdec_value_to_number_7 (SwfdecAsContext *cx, jsval val, double *d) +{ + if (JSVAL_IS_OBJECT (val)) { + *d = *cx->runtime->jsNaN; + return JS_TRUE; + } else if (JSVAL_IS_STRING (val) && + JS_GetStringLength (JSVAL_TO_STRING (val)) == 0) { + *d = *cx->runtime->jsNaN; + return JS_TRUE; + } else { + return JS_ValueToNumber (cx, val, d); + } +} +#endif + +/*** ALL THE ACTION IS HERE ***/ + +static void +swfdec_action_stop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie = swfdec_action_get_target (cx); + if (movie) + movie->stopped = TRUE; + else + SWFDEC_ERROR ("no movie to stop"); +} + +static void +swfdec_action_play (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie = swfdec_action_get_target (cx); + if (movie) + movie->stopped = FALSE; + else + SWFDEC_ERROR ("no movie to play"); +} + +static void +swfdec_action_next_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie = swfdec_action_get_target (cx); + if (movie) { + if (movie->frame + 1 < movie->n_frames) { + swfdec_movie_goto (movie, movie->frame + 1); + } else { + SWFDEC_INFO ("can't execute nextFrame, already at last frame"); + } + } else { + SWFDEC_ERROR ("no movie to nextFrame on"); + } +} + +static void +swfdec_action_previous_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie = swfdec_action_get_target (cx); + if (movie) { + if (movie->frame > 0) { + swfdec_movie_goto (movie, movie->frame - 1); + } else { + SWFDEC_INFO ("can't execute previousFrame, already at first frame"); + } + } else { + SWFDEC_ERROR ("no movie to previousFrame on"); + } +} + +static void +swfdec_action_goto_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie = swfdec_action_get_target (cx); + guint frame; + + if (len != 2) { + SWFDEC_ERROR ("GotoFrame action length invalid (is %u, should be 2", len); + return; + } + frame = GUINT16_FROM_LE (*((guint16 *) data)); + if (movie) { + swfdec_movie_goto (movie, frame); + movie->stopped = TRUE; + } else { + SWFDEC_ERROR ("no movie to goto on"); + } +} + +static void +swfdec_action_goto_label (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie = swfdec_action_get_target (cx); + + if (!memchr (data, 0, len)) { + SWFDEC_ERROR ("GotoLabel action does not specify a string"); + return; + } + + if (SWFDEC_IS_SPRITE_MOVIE (movie)) { + int frame = swfdec_sprite_get_frame (SWFDEC_SPRITE_MOVIE (movie)->sprite, (const char *) data); + if (frame == -1) + return; + swfdec_movie_goto (movie, frame); + movie->stopped = TRUE; + } else { + SWFDEC_ERROR ("no movie to goto on"); + } +} + +#if 0 +static int +swfdec_value_to_frame (SwfdecAsContext *cx, SwfdecMovie *movie, jsval val) +{ + int frame; + + if (JSVAL_IS_STRING (val)) { + const char *name = swfdec_js_to_string (cx, val); + char *end; + if (name == NULL || + !SWFDEC_IS_SPRITE_MOVIE (movie)) + return -1; + if (strchr (name, ':')) { + SWFDEC_ERROR ("FIXME: handle targets"); + } + /* treat valid encoded numbers as numbers, otherwise assume it's a frame label */ + frame = strtol (name, &end, 0); + if (*end != '\0') + frame = swfdec_sprite_get_frame (SWFDEC_SPRITE_MOVIE (movie)->sprite, name); + else + frame--; + } else if (JSVAL_IS_INT (val)) { + return JSVAL_TO_INT (val) - 1; + } else if (JSVAL_IS_DOUBLE (val)) { + return (int) *JSVAL_TO_DOUBLE (val) - 1; + } else { + /* FIXME: how do we treat undefined etc? */ + frame = -1; + } + return frame; +} + +static void +swfdec_action_goto_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecBits bits; + guint bias; + gboolean play; + jsval val; + SwfdecMovie *movie; + + swfdec_bits_init_data (&bits, data, len); + if (swfdec_bits_getbits (&bits, 6)) { + SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0"); + } + bias = swfdec_bits_getbit (&bits); + play = swfdec_bits_getbit (&bits); + if (bias) { + bias = swfdec_bits_get_u16 (&bits); + } + val = cx->fp->sp[-1]; + cx->fp->sp--; + movie = swfdec_action_get_target (cx); + /* now set it */ + if (movie) { + int frame = swfdec_value_to_frame (cx, movie, val); + if (frame < 0) + return JS_TRUE; + frame += bias; + frame = CLAMP (frame, 0, (int) movie->n_frames - 1); + swfdec_movie_goto (movie, frame); + movie->stopped = !play; + } else { + SWFDEC_ERROR ("no movie to GotoFrame2 on"); + } + return JS_TRUE; +} +#endif + +static void +swfdec_script_skip_actions (SwfdecAsContext *cx, guint jump) +{ + SwfdecScript *script = cx->frame->script; + guint8 *pc = cx->frame->pc; + guint8 *endpc = script->buffer->data + script->buffer->length; + + /* jump instructions */ + do { + if (pc >= endpc) + break; + if (*pc & 0x80) { + if (pc + 2 >= endpc) + break; + pc += 3 + GUINT16_FROM_LE (*((guint16 *) (pc + 1))); + } else { + pc++; + } + } while (jump-- > 0); + cx->frame->pc = pc; +} + +#if 0 +static void +swfdec_action_wait_for_frame2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval val; + SwfdecMovie *movie; + + if (len != 1) { + SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data"); + return JS_FALSE; + } + val = cx->fp->sp[-1]; + cx->fp->sp--; + movie = swfdec_action_get_target (cx); + if (movie) { + int frame = swfdec_value_to_frame (cx, movie, val); + guint jump = data[2]; + guint loaded; + if (frame < 0) + return JS_TRUE; + if (SWFDEC_IS_ROOT_MOVIE (movie)) { + SwfdecDecoder *dec = SWFDEC_ROOT_MOVIE (movie)->decoder; + loaded = dec->frames_loaded; + g_assert (loaded <= movie->n_frames); + } else { + loaded = movie->n_frames; + } + if (loaded < (guint) frame) + swfdec_script_skip_actions (cx, jump); + } else { + SWFDEC_ERROR ("no movie to WaitForFrame2 on"); + } + return JS_TRUE; +} +#endif + +static void +swfdec_action_wait_for_frame (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie; + guint frame, jump, loaded; + + if (len != 3) { + SWFDEC_ERROR ("WaitForFrame action length invalid (is %u, should be 3", len); + return; + } + movie = swfdec_action_get_target (cx); + if (movie == NULL) { + SWFDEC_ERROR ("no movie for WaitForFrame"); + return; + } + + frame = GUINT16_FROM_LE (*((guint16 *) data)); + jump = data[2]; + if (SWFDEC_IS_ROOT_MOVIE (movie)) { + SwfdecDecoder *dec = SWFDEC_ROOT_MOVIE (movie)->decoder; + loaded = dec->frames_loaded; + g_assert (loaded <= movie->n_frames); + } else { + loaded = movie->n_frames; + } + if (loaded < frame) + swfdec_script_skip_actions (cx, jump); +} + +#if 0 +static void +swfdec_action_constant_pool (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecConstantPool *pool; + + pool = swfdec_constant_pool_new_from_action (data, len); + if (pool == NULL) + return JS_FALSE; + if (cx->fp->constant_pool) + swfdec_constant_pool_free (cx->fp->constant_pool); + cx->fp->constant_pool = pool; + return JS_TRUE; +} + +static void +swfdec_action_push (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + /* FIXME: supply API for this */ + SwfdecBits bits; + guint stackspace = cx->fp->spend - cx->fp->sp; + + swfdec_bits_init_data (&bits, data, len); + while (swfdec_bits_left (&bits) && stackspace-- > 0) { + guint type = swfdec_bits_get_u8 (&bits); + SWFDEC_LOG ("push type %u", type); + switch (type) { + case 0: /* string */ + { + const char *s = swfdec_bits_skip_string (&bits); + if (!swfdec_action_push_string (cx, s)) + return JS_FALSE; + break; + } + case 1: /* float */ + { + double d = swfdec_bits_get_float (&bits); + if (!JS_NewDoubleValue (cx, d, cx->fp->sp)) + return JS_FALSE; + cx->fp->sp++; + break; + } + case 2: /* null */ + *cx->fp->sp++ = JSVAL_NULL; + break; + case 3: /* undefined */ + *cx->fp->sp++ = JSVAL_VOID; + break; + case 4: /* register */ + { + guint regnum = swfdec_bits_get_u8 (&bits); + if (!swfdec_action_has_register (cx, regnum)) { + SWFDEC_ERROR ("cannot Push register %u: not enough registers", regnum); + return JS_FALSE; + } + *cx->fp->sp++ = cx->fp->vars[regnum]; + break; + } + case 5: /* boolean */ + *cx->fp->sp++ = swfdec_bits_get_u8 (&bits) ? JSVAL_TRUE : JSVAL_FALSE; + break; + case 6: /* double */ + { + double d = swfdec_bits_get_double (&bits); + if (!JS_NewDoubleValue (cx, d, cx->fp->sp)) + return JS_FALSE; + cx->fp->sp++; + break; + } + case 7: /* 32bit int */ + { + int i = swfdec_bits_get_u32 (&bits); + *cx->fp->sp++ = INT_TO_JSVAL (i); + break; + } + case 8: /* 8bit ConstantPool address */ + { + guint i = swfdec_bits_get_u8 (&bits); + SwfdecConstantPool *pool = cx->fp->constant_pool; + if (pool == NULL) { + SWFDEC_ERROR ("no constant pool to push from"); + return JS_FALSE; + } + if (i >= swfdec_constant_pool_size (pool)) { + SWFDEC_ERROR ("constant pool index %u too high - only %u elements", + i, swfdec_constant_pool_size (pool)); + return JS_FALSE; + } + if (!swfdec_action_push_string (cx, swfdec_constant_pool_get (pool, i))) + return JS_FALSE; + break; + } + case 9: /* 16bit ConstantPool address */ + { + guint i = swfdec_bits_get_u16 (&bits); + SwfdecConstantPool *pool = cx->fp->constant_pool; + if (pool == NULL) { + SWFDEC_ERROR ("no constant pool to push from"); + return JS_FALSE; + } + if (i >= swfdec_constant_pool_size (pool)) { + SWFDEC_ERROR ("constant pool index %u too high - only %u elements", + i, swfdec_constant_pool_size (pool)); + return JS_FALSE; + } + if (!swfdec_action_push_string (cx, swfdec_constant_pool_get (pool, i))) + return JS_FALSE; + break; + } + default: + SWFDEC_ERROR ("Push: type %u not implemented", type); + return JS_FALSE; + } + } + return swfdec_bits_left (&bits) ? JS_FALSE : JS_TRUE; +} + +static void +swfdec_action_get_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *s; + + s = swfdec_js_to_string (cx, cx->fp->sp[-1]); + if (s == NULL) + return JS_FALSE; + cx->fp->sp[-1] = swfdec_js_eval (cx, NULL, s); +#ifdef SWFDEC_WARN_MISSING_PROPERTIES + if (cx->fp->sp[-1] == JSVAL_VOID) { + SWFDEC_WARNING ("no variable named %s", s); + } +#endif + return JS_TRUE; +} + +static void +swfdec_action_set_variable (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *s; + + s = swfdec_js_to_string (cx, cx->fp->sp[-2]); + if (s == NULL) + return JS_FALSE; + + swfdec_js_eval_set (cx, NULL, s, cx->fp->sp[-1]); + cx->fp->sp -= 2; + return JS_TRUE; +} + +static void +swfdec_action_trace (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecPlayer *player = JS_GetContextPrivate (cx); + const char *bytes; + + bytes = swfdec_js_to_string (cx, cx->fp->sp[-1]); + cx->fp->sp--; + if (bytes == NULL) + return JS_TRUE; + + swfdec_player_trace (player, bytes); + return JS_TRUE; +} + +/** + * swfdec_action_invoke: + * @cx: the #SwfdecAsContext + * @n_args: number of arguments + * + * This function is similar to js_Invoke, however it uses a reversed stack + * order. sp[-1] has to be the function to call, sp[-2] will be the object the + * function is called on, sp[-3] is the first argument, followed by the rest of + * the arguments. The function removes all of these argumends from the stack + * and pushes the return value on top. + * + * Returns: JS_TRUE on success, JS_FALSE on failure. + **/ +static void +swfdec_action_call (SwfdecAsContext *cx, guint n_args, guint flags) +{ + JSStackFrame *fp = cx->fp; + int i, j; + jsval tmp; + guint stacksize; + + stacksize = fp->sp - fp->spbase; + g_assert (stacksize >= 2); + if (n_args + 2 > stacksize) { + SWFDEC_WARNING ("broken script. Want %u arguments, only got %u", n_args, stacksize - 2); + n_args = stacksize - 2; + if (!swfdec_script_ensure_stack (cx, n_args + 2)) + return JS_FALSE; + } + + j = -1; + i = - (n_args + 2); + while (i < j) { + tmp = fp->sp[j]; + fp->sp[j] = fp->sp[i]; + fp->sp[i] = tmp; + j--; + i++; + } + return js_Invoke (cx, n_args, flags); +} + +/* FIXME: lots of overlap with swfdec_action_call_method */ +static void +swfdec_action_call_function (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSStackFrame *fp = cx->fp; + const char *s; + guint32 n_args; + JSObject *obj, *pobj; + JSProperty *prop; + jsval fun; + JSAtom *atom; + + if (!swfdec_script_ensure_stack (cx, 2)) + return JS_FALSE; + s = swfdec_js_to_string (cx, fp->sp[-1]); + if (s == NULL) + return JS_FALSE; + if (!JS_ValueToECMAUint32 (cx, fp->sp[-2], &n_args)) + return JS_FALSE; + + if (!(atom = js_Atomize (cx, s, strlen (s), 0)) || + !js_FindProperty (cx, (jsid) atom, &obj, &pobj, &prop)) + return JS_FALSE; + if (!JS_GetProperty (cx, obj, s, &fun)) + return JS_FALSE; + if (!JSVAL_IS_OBJECT (fun)) { + /* FIXME: figure out what class we operate on */ + SWFDEC_WARNING ("%s is not a function", s); + } + fp->sp[-1] = fun; + fp->sp[-2] = OBJECT_TO_JSVAL (obj); + swfdec_action_call (cx, n_args, 0); + return JS_TRUE; +} + +static void +swfdec_action_call_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSStackFrame *fp = cx->fp; + const char *s; + guint32 n_args; + JSObject *obj; + jsval fun; + + if (!swfdec_script_ensure_stack (cx, 3)) + return JS_FALSE; + if (fp->sp[-1] == JSVAL_VOID) { + s = ""; + } else { + s = swfdec_js_to_string (cx, fp->sp[-1]); + if (s == NULL) + return JS_FALSE; + } + if (!JS_ValueToECMAUint32 (cx, fp->sp[-3], &n_args)) + return JS_FALSE; + + if (!JS_ValueToObject (cx, fp->sp[-2], &obj)) + return JS_FALSE; + if (obj == NULL) + goto fail; + if (s[0] == '\0') { + fun = OBJECT_TO_JSVAL (obj); + } else { + if (!JS_GetProperty (cx, obj, s, &fun)) + return JS_FALSE; + if (!JSVAL_IS_OBJECT (fun)) { + SWFDEC_WARNING ("%s:%s is not a function", JS_GetClass (obj)->name, s); + } + } + fp->sp--; + fp->sp[-1] = fun; + fp->sp[-2] = OBJECT_TO_JSVAL (obj); + swfdec_action_call (cx, n_args, 0); + return JS_TRUE; + +fail: + fp->sp -= 2 + n_args; + fp->sp[-1] = JSVAL_VOID; + return JS_TRUE; +} + +static void +swfdec_action_pop (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + cx->fp->sp--; + return JS_TRUE; +} + +static const char * +swfdec_eval_jsval (SwfdecAsContext *cx, JSObject *obj, jsval *val) +{ + if (JSVAL_IS_STRING (*val)) { + const char *bytes = swfdec_js_to_string (cx, *val); + if (bytes == NULL) + return NULL; + *val = swfdec_js_eval (cx, obj, bytes); + return bytes; + } else { + if (obj == NULL) { + obj = OBJ_THIS_OBJECT (cx, cx->fp->scopeChain); + } + *val = OBJECT_TO_JSVAL (obj); + return "."; + } +} + +static const char *properties[22] = { + "_x", "_y", "_xscale", "_yscale", "_currentframe", + "_totalframes", "_alpha", "_visible", "_width", "_height", + "_rotation", "_target", "_framesloaded", "_name", "_droptarget", + "_url", "_highquality", "_focusrect", "_soundbuftime", "_quality", + "_xmouse", "_ymouse" +}; + +static void +swfdec_action_get_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval val; + SwfdecMovie *movie; + JSObject *jsobj; + guint32 id; + const char *bytes; + + if (!JS_ValueToECMAUint32 (cx, cx->fp->sp[-1], &id)) + return JS_FALSE; + val = cx->fp->sp[-2]; + bytes = swfdec_eval_jsval (cx, NULL, &val); + if (id > (((SwfdecScript *) cx->fp->swf)->version > 4 ? 21 : 18)) { + SWFDEC_WARNING ("trying to SetProperty %u, not allowed", id); + goto out; + } + + if (bytes == NULL) + return JS_FALSE; + if (*bytes == '\0') { + JSObject *pobj; + JSProperty *prop; + JSAtom *atom = js_Atomize (cx, properties[id], strlen (properties[id]), 0); + if (atom == NULL) + return JS_FALSE; + if (!js_FindProperty (cx, (jsid) atom, &jsobj, &pobj, &prop)) + return JS_FALSE; + if (!prop) + return JS_FALSE; + if (!OBJ_GET_PROPERTY (cx, jsobj, (jsid) prop->id, &val)) + return JS_FALSE; + } else { + movie = swfdec_scriptable_from_jsval (cx, val, SWFDEC_TYPE_MOVIE); + if (movie == NULL) { + SWFDEC_WARNING ("specified target does not reference a movie clip"); + goto out; + } + + jsobj = JSVAL_TO_OBJECT (val); + val = JSVAL_VOID; + + if (!JS_GetProperty (cx, jsobj, properties[id], &val)) + return JS_FALSE; + } + +out: + cx->fp->sp -= 1; + cx->fp->sp[-1] = val; + return JS_TRUE; +} + +static void +swfdec_action_set_property (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval val; + SwfdecMovie *movie; + JSObject *jsobj; + guint32 id; + const char *bytes; + + val = cx->fp->sp[-3]; + if (!JS_ValueToECMAUint32 (cx, cx->fp->sp[-2], &id)) + return JS_FALSE; + bytes = swfdec_eval_jsval (cx, NULL, &val); + if (!bytes) + return JS_FALSE; + if (id > (((SwfdecScript *) cx->fp->swf)->version > 4 ? 21 : 18)) { + SWFDEC_WARNING ("trying to SetProperty %u, not allowed", id); + goto out; + } + if (*bytes == '\0' || *bytes == '.') + val = OBJECT_TO_JSVAL (cx->fp->varobj); + movie = swfdec_scriptable_from_jsval (cx, val, SWFDEC_TYPE_MOVIE); + if (movie == NULL) { + SWFDEC_WARNING ("specified target does not reference a movie clip"); + goto out; + } + jsobj = JSVAL_TO_OBJECT (val); + + if (!JS_SetProperty (cx, jsobj, properties[id], &cx->fp->sp[-1])) + return JS_FALSE; + +out: + cx->fp->sp -= 3; + return JS_TRUE; +} + +static void +swfdec_action_binary (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval lval, rval; + double l, r; + + rval = cx->fp->sp[-1]; + lval = cx->fp->sp[-2]; + if (((SwfdecScript *) cx->fp->swf)->version < 7) { + l = swfdec_value_to_number (cx, lval); + r = swfdec_value_to_number (cx, rval); + } else { + if (!swfdec_value_to_number_7 (cx, lval, &l) || + !swfdec_value_to_number_7 (cx, rval, &r)) + return JS_FALSE; + } + cx->fp->sp--; + switch (action) { + case 0x0a: + l = l + r; + break; + case 0x0b: + l = l - r; + break; + case 0x0c: + l = l * r; + break; + case 0x0d: + if (((SwfdecScript *) cx->fp->swf)->version < 5) { + if (r == 0) { + JSString *str = JS_InternString (cx, "#ERROR#"); + if (str == NULL) + return JS_FALSE; + cx->fp->sp[-1] = STRING_TO_JSVAL (str); + return JS_TRUE; + } + } else if (((SwfdecScript *) cx->fp->swf)->version < 7) { + if (isnan (r)) + r = 0; + } + l = l / r; + break; + default: + g_assert_not_reached (); + return r; + } + return JS_NewNumberValue (cx, l, &cx->fp->sp[-1]); +} + +static JSString * +swfdec_action_to_string_5 (SwfdecAsContext *cx, jsval val) +{ + if (JSVAL_IS_VOID (val)) + return cx->runtime->emptyString; + return js_ValueToString (cx, val); +} + +static void +swfdec_action_add2_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval rval, lval; + gboolean cond; + + rval = cx->fp->sp[-1]; + lval = cx->fp->sp[-2]; + if ((cond = JSVAL_IS_STRING (lval)) || JSVAL_IS_STRING (rval)) { + JSString *str, *str2; + if (cond) { + str = JSVAL_TO_STRING (lval); + if ((str2 = swfdec_action_to_string_5 (cx, rval)) == NULL) + return JS_FALSE; + } else { + str2 = JSVAL_TO_STRING (rval); + if ((str = swfdec_action_to_string_5 (cx, lval)) == NULL) + return JS_FALSE; + } + str = js_ConcatStrings (cx, str, str2); + if (!str) + return JS_FALSE; + cx->fp->sp--; + cx->fp->sp[-1] = STRING_TO_JSVAL (str); + } else { + double d, d2; + d = swfdec_value_to_number (cx, lval); + d2 = swfdec_value_to_number (cx, rval); + d += d2; + cx->fp->sp--; + return JS_NewNumberValue(cx, d, &cx->fp->sp[-1]); + } + return JS_TRUE; +} + +static void +swfdec_action_add2_7 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval rval, lval; + gboolean cond; + + rval = cx->fp->sp[-1]; + lval = cx->fp->sp[-2]; + if ((cond = JSVAL_IS_STRING (lval)) || JSVAL_IS_STRING (rval)) { + JSString *str, *str2; + if (cond) { + str = JSVAL_TO_STRING (lval); + if ((str2 = js_ValueToString (cx, rval)) == NULL) + return JS_FALSE; + } else { + str2 = JSVAL_TO_STRING (rval); + if ((str = js_ValueToString (cx, lval)) == NULL) + return JS_FALSE; + } + str = js_ConcatStrings (cx, str, str2); + if (!str) + return JS_FALSE; + cx->fp->sp--; + cx->fp->sp[-1] = STRING_TO_JSVAL (str); + } else { + double d, d2; + if (!swfdec_value_to_number_7 (cx, lval, &d) || + !swfdec_value_to_number_7 (cx, rval, &d2)) + return JS_FALSE; + d += d2; + cx->fp->sp--; + return JS_NewNumberValue(cx, d, &cx->fp->sp[-1]); + } + return JS_TRUE; +} + +static void +swfdec_action_get_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *s; + jsval o; + + s = swfdec_js_to_string (cx, cx->fp->sp[-1]); + if (s == NULL) + return JS_FALSE; + + o = cx->fp->sp[-2]; + if (JSVAL_IS_OBJECT (o) && !JSVAL_IS_NULL (o)) { + if (!JS_GetProperty (cx, JSVAL_TO_OBJECT (o), s, &cx->fp->sp[-2])) + return JS_FALSE; +#ifdef SWFDEC_WARN_MISSING_PROPERTIES + if (cx->fp->sp[-2] == JSVAL_VOID) { + const JSClass *clasp = JS_GetClass (JSVAL_TO_OBJECT (o)); + if (clasp != &js_ObjectClass) { + SWFDEC_WARNING ("no variable named %s:%s", clasp->name, s); + } + } +#endif + } else { + cx->fp->sp[-2] = JSVAL_VOID; + } + cx->fp->sp--; + return JS_TRUE; +} + +static void +swfdec_action_set_member (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *s; + + s = swfdec_js_to_string (cx, cx->fp->sp[-2]); + if (s == NULL) + return JS_FALSE; + + if (JSVAL_IS_OBJECT (cx->fp->sp[-3]) && !JSVAL_IS_NULL (cx->fp->sp[-3])) { + if (!JS_SetProperty (cx, JSVAL_TO_OBJECT (cx->fp->sp[-3]), s, &cx->fp->sp[-1])) + return JS_FALSE; + } + cx->fp->sp -= 3; + return JS_TRUE; +} + +static void +swfdec_action_new_comparison_6 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval lval, rval; + double d, d2; + + rval = cx->fp->sp[-1]; + lval = cx->fp->sp[-2]; + cx->fp->sp--; + d = swfdec_value_to_number (cx, lval); + d2 = swfdec_value_to_number (cx, rval); + if (action == 0x48) + cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (d < d2); + else + cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (d > d2); + return JS_TRUE; +} + +static void +swfdec_action_new_comparison_7 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval lval, rval; + + rval = cx->fp->sp[-1]; + lval = cx->fp->sp[-2]; + cx->fp->sp--; + if (JSVAL_IS_VOID (rval) || JSVAL_IS_VOID (lval)) { + cx->fp->sp[-1] = JSVAL_VOID; + } else if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { + int comp = JS_CompareStrings (JSVAL_TO_STRING (lval), JSVAL_TO_STRING (rval)); + cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (action == 0x48 ? comp < 0 : comp > 0); + } else { + double d, d2; + if (!JS_ValueToNumber(cx, lval, &d) || + !JS_ValueToNumber(cx, rval, &d2)) + return JS_FALSE; + cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (action == 0x48 ? d < d2 : d > d2); + } + return JS_TRUE; +} + +static void +swfdec_action_not_4 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double d; + + d = swfdec_value_to_number (cx, cx->fp->sp[-1]); + cx->fp->sp[-1] = INT_TO_JSVAL (d == 0 ? 1 : 0); + return JS_TRUE; +} + +static void +swfdec_action_not_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double d; + + d = swfdec_value_to_number (cx, cx->fp->sp[-1]); + cx->fp->sp[-1] = d == 0 ? JSVAL_TRUE : JSVAL_FALSE; + return JS_TRUE; +} + +static void +swfdec_action_jump (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + if (len != 2) { + SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len); + return JS_FALSE; + } + cx->fp->pc += 5 + GINT16_FROM_LE (*((gint16*) data)); + return JS_TRUE; +} + +static void +swfdec_action_if (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double d; + + if (len != 2) { + SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len); + return JS_FALSE; + } + d = swfdec_value_to_number (cx, cx->fp->sp[-1]); + cx->fp->sp--; + if (d != 0) + cx->fp->pc += 5 + GINT16_FROM_LE (*((gint16*) data)); + return JS_TRUE; +} + +static void +swfdec_action_decrement (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double d; + + d = swfdec_value_to_number (cx, cx->fp->sp[-1]); + d--; + return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); +} + +static void +swfdec_action_increment (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double d; + + d = swfdec_value_to_number (cx, cx->fp->sp[-1]); + d++; + return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); +} + +static void +swfdec_action_get_url (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie; + SwfdecBits bits; + const char *url, *target; + + swfdec_bits_init_data (&bits, data, len); + url = swfdec_bits_skip_string (&bits); + target = swfdec_bits_skip_string (&bits); + if (swfdec_bits_left (&bits)) { + SWFDEC_WARNING ("leftover bytes in GetURL action"); + } + movie = swfdec_action_get_target (cx); + if (movie) + swfdec_root_movie_load (SWFDEC_ROOT_MOVIE (movie->root), url, target); + else + SWFDEC_WARNING ("no movie to load"); + return JS_TRUE; +} + +static void +swfdec_action_get_url2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *target, *url; + guint method; + SwfdecMovie *movie; + + if (len != 1) { + SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len); + return JS_FALSE; + } + target = swfdec_js_to_string (cx, cx->fp->sp[-1]); + url = swfdec_js_to_string (cx, cx->fp->sp[-2]); + if (target == NULL || url == NULL) + return JS_FALSE; + method = data[0] >> 6; + if (method == 3) { + SWFDEC_ERROR ("GetURL method 3 invalid"); + method = 0; + } + if (method) { + SWFDEC_ERROR ("FIXME: implement encoding variables using %s", method == 1 ? "GET" : "POST"); + } + if (data[0] & 2) { + SWFDEC_ERROR ("FIXME: implement LoadTarget"); + } + if (data[0] & 1) { + SWFDEC_ERROR ("FIXME: implement LoadVariables"); + } + movie = swfdec_action_get_target (cx); + if (movie) + swfdec_root_movie_load (SWFDEC_ROOT_MOVIE (movie->root), url, target); + else + SWFDEC_WARNING ("no movie to load"); + cx->fp->sp -= 2; + return JS_TRUE; +} + +static void +swfdec_action_string_add (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSString *lval, *rval; + + rval = JS_ValueToString (cx, cx->fp->sp[-1]); + lval = JS_ValueToString (cx, cx->fp->sp[-2]); + if (lval == NULL || rval == NULL) + return FALSE; + lval = JS_ConcatStrings (cx, lval, rval); + if (lval == NULL) + return FALSE; + cx->fp->sp--; + cx->fp->sp[-1] = STRING_TO_JSVAL (lval); + return JS_TRUE; +} + +static void +swfdec_action_push_duplicate (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + cx->fp->sp++; + cx->fp->sp[-1] = cx->fp->sp[-2]; + return JS_TRUE; +} + +static void +swfdec_action_random_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + gint32 max, result; + + if (!JS_ValueToECMAInt32 (cx, cx->fp->sp[-1], &max)) + return JS_FALSE; + + if (max <= 0) + result = 0; + else + result = g_random_int_range (0, max); + + return JS_NewNumberValue(cx, result, &cx->fp->sp[-1]); +} + +static void +swfdec_action_old_compare (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval rval, lval; + double l, r; + void cond; + + rval = cx->fp->sp[-1]; + lval = cx->fp->sp[-2]; + l = swfdec_value_to_number (cx, lval); + r = swfdec_value_to_number (cx, rval); + switch (action) { + case 0x0e: + cond = l == r; + break; + case 0x0f: + cond = l < r; + break; + default: + g_assert_not_reached (); + return JS_FALSE; + } + cx->fp->sp--; + if (((SwfdecScript *) cx->fp->swf)->version < 5) { + cx->fp->sp[-1] = INT_TO_JSVAL (cond ? 1 : 0); + } else { + cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (cond); + } + return JS_TRUE; +} + +static void +swfdec_action_equals2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval rval, lval; + int ltag, rtag; + void cond; + + rval = cx->fp->sp[-1]; + lval = cx->fp->sp[-2]; + ltag = JSVAL_TAG(lval); + rtag = JSVAL_TAG(rval); + if (ltag == rtag) { + if (ltag == JSVAL_STRING) { + cond = js_CompareStrings (JSVAL_TO_STRING (lval), JSVAL_TO_STRING (rval)) == 0; + } else if (ltag == JSVAL_DOUBLE) { + cond = *JSVAL_TO_DOUBLE(lval) == *JSVAL_TO_DOUBLE(rval); + } else { + cond = lval == rval; + } + } else { + if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { + cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)); + } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { + cond = JS_FALSE; + } else { + if (ltag == JSVAL_OBJECT) { + if (!OBJ_DEFAULT_VALUE (cx, JSVAL_TO_OBJECT(lval), 0, &lval)) + return JS_FALSE; + ltag = JSVAL_TAG(lval); + } else if (rtag == JSVAL_OBJECT) { + if (!OBJ_DEFAULT_VALUE (cx, JSVAL_TO_OBJECT(rval), 0, &rval)) + return JS_FALSE; + rtag = JSVAL_TAG(rval); + } + if (ltag == JSVAL_STRING && rtag == JSVAL_STRING) { + cond = js_CompareStrings (JSVAL_TO_STRING (lval), JSVAL_TO_STRING (rval)) == 0; + } else { + double d, d2; + if (!JS_ValueToNumber (cx, lval, &d) || + !JS_ValueToNumber (cx, rval, &d2)) + return JS_FALSE; + cond = d == d2; + } + } + } + cx->fp->sp--; + cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (cond); + return JS_TRUE; +} + +static void +swfdec_action_do_set_target (SwfdecAsContext *cx, JSObject *target) +{ + JSObject *with; + + /* FIXME: this whole function stops working the moment it's used together + * with With */ + with = js_NewObject(cx, &js_WithClass, target, cx->fp->scopeChain); + if (!with) + return JS_FALSE; + cx->fp->scopeChain = with; + return JS_TRUE; +} + +static void +swfdec_action_do_unset_target (SwfdecAsContext *cx) +{ + if (JS_GetClass (cx->fp->scopeChain) != &js_WithClass) { + SWFDEC_ERROR ("Cannot unset target: scope chain contains no with object"); + return JS_TRUE; + } + cx->fp->scopeChain = JS_GetParent (cx, cx->fp->scopeChain); + return JS_TRUE; +} + +static void +swfdec_action_set_target (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval target; + + if (!memchr (data, 0, len)) { + SWFDEC_ERROR ("SetTarget action does not specify a string"); + return JS_FALSE; + } + if (*data == '\0') + return swfdec_action_do_unset_target (cx); + target = swfdec_js_eval (cx, NULL, (const char *) data); + if (!JSVAL_IS_OBJECT (target) || JSVAL_IS_NULL (target)) { + SWFDEC_WARNING ("target is not an object"); + return JS_TRUE; + } + return swfdec_action_do_set_target (cx, JSVAL_TO_OBJECT (target)); +} + +static void +swfdec_action_set_target2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval val; + + val = cx->fp->sp[-1]; + cx->fp->sp--; + if (!JSVAL_IS_OBJECT (val) || JSVAL_IS_NULL (val)) { + SWFDEC_WARNING ("target is not an object"); + return JS_TRUE; + } + return swfdec_action_do_set_target (cx, JSVAL_TO_OBJECT (val)); +} + +static void +swfdec_action_start_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSStackFrame *fp = cx->fp; + guint n_args = 1; + + if (!swfdec_script_ensure_stack (cx, 3)) + return JS_FALSE; + if (!swfdec_eval_jsval (cx, NULL, &fp->sp[-1])) + return JS_FALSE; + if (swfdec_value_to_number (cx, fp->sp[-3])) { + jsval tmp; + if (!swfdec_script_ensure_stack (cx, 7)) + return JS_FALSE; + n_args = 5; + /* yay for order */ + tmp = fp->sp[-4]; + fp->sp[-4] = fp->sp[-7]; + fp->sp[-7] = tmp; + tmp = fp->sp[-6]; + fp->sp[-6] = fp->sp[-5]; + fp->sp[-5] = tmp; + } + if (!JSVAL_IS_OBJECT (fp->sp[-1]) || JSVAL_IS_NULL (fp->sp[-1])) { + fp->sp -= n_args + 2; + return JS_TRUE; + } + fp->sp[-3] = fp->sp[-2]; + fp->sp[-2] = fp->sp[-1]; + if (!JS_GetProperty (cx, JSVAL_TO_OBJECT (fp->sp[-2]), "startDrag", &fp->sp[-1])) + return JS_FALSE; + swfdec_action_call (cx, n_args, 0); + fp->sp--; + return JS_TRUE; +} + +static void +swfdec_action_end_drag (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecPlayer *player = JS_GetContextPrivate (cx); + swfdec_player_set_drag_movie (player, NULL, FALSE, NULL); + return JS_TRUE; +} + +static void +swfdec_action_stop_sounds (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecPlayer *player = JS_GetContextPrivate (cx); + + swfdec_player_stop_all_sounds (player); + return JS_TRUE; +} + +static void +swfdec_action_new_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSStackFrame *fp = cx->fp; + jsval constructor; + JSObject *object; + guint n_args; + const char *name; + + if (!swfdec_script_ensure_stack (cx, 2)) + return JS_FALSE; + constructor = fp->sp[-1]; + name = swfdec_eval_jsval (cx, NULL, &constructor); + if (name == NULL) + return JS_FALSE; + if (!JS_ValueToECMAUint32 (cx, fp->sp[-2], &n_args)) + return JS_FALSE; + if (constructor == JSVAL_VOID) { + SWFDEC_WARNING ("no constructor for %s", name); + } + fp->sp[-1] = constructor; + + if (!swfdec_js_construct_object (cx, NULL, constructor, &object)) + return JS_FALSE; + if (object == NULL) + goto fail; + fp->sp[-2] = OBJECT_TO_JSVAL (object); + if (!swfdec_action_call (cx, n_args, JSINVOKE_CONSTRUCT)) + return JS_FALSE; + fp->sp[-1] = OBJECT_TO_JSVAL (object); + return JS_TRUE; + +fail: + fp->sp -= n_args + 1; + fp->sp[-1] = JSVAL_VOID; + return JS_TRUE; +} + +static void +swfdec_action_new_method (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSStackFrame *fp = cx->fp; + const char *s; + guint32 n_args; + JSObject *object; + jsval constructor; + + if (!swfdec_script_ensure_stack (cx, 3)) + return JS_FALSE; + s = swfdec_js_to_string (cx, fp->sp[-1]); + if (s == NULL) + return JS_FALSE; + if (!JS_ValueToECMAUint32 (cx, fp->sp[-3], &n_args)) + return JS_FALSE; + + if (!JS_ValueToObject (cx, fp->sp[-2], &object)) + return JS_FALSE; + if (object == NULL) + goto fail; + if (s[0] == '\0') { + constructor = OBJECT_TO_JSVAL (object); + } else { + if (!JS_GetProperty (cx, object, s, &constructor)) + return JS_FALSE; + if (!JSVAL_IS_OBJECT (constructor)) { + SWFDEC_WARNING ("%s:%s is not a function", JS_GetClass (object)->name, s); + } + } + fp->sp[-1] = OBJECT_TO_JSVAL (constructor); + if (!swfdec_js_construct_object (cx, NULL, constructor, &object)) + return JS_FALSE; + if (object == NULL) + goto fail; + fp->sp[-2] = OBJECT_TO_JSVAL (object); + if (!swfdec_action_call (cx, n_args, JSINVOKE_CONSTRUCT)) + return JS_FALSE; + fp->sp[-1] = OBJECT_TO_JSVAL (object); + return JS_TRUE; + +fail: + fp->sp -= 2 + n_args; + fp->sp[-1] = JSVAL_VOID; + return JS_TRUE; +} + +static void +swfdec_action_init_object (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSStackFrame *fp = cx->fp; + JSObject *object; + guint n_args; + gulong i; + + if (!JS_ValueToECMAUint32 (cx, fp->sp[-1], &n_args)) + return JS_FALSE; + if (!swfdec_script_ensure_stack (cx, 2 * n_args + 1)) + return JS_FALSE; + + object = JS_NewObject (cx, &js_ObjectClass, NULL, NULL); + if (object == NULL) + return JS_FALSE; + for (i = 0; i < n_args; i++) { + const char *s = swfdec_js_to_string (cx, fp->sp[-3 - 2 * i]); + if (s == NULL) + return JS_FALSE; + if (!JS_SetProperty (cx, object, s, &fp->sp[-2 - 2 * i])) + return JS_FALSE; + } + fp->sp -= 2 * n_args; + fp->sp[-1] = OBJECT_TO_JSVAL (object); + return JS_TRUE; +} + +static void +swfdec_action_init_array (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSStackFrame *fp = cx->fp; + JSObject *array; + int i, j; + guint n_items; + + if (!JS_ValueToECMAUint32 (cx, fp->sp[-1], &n_items)) + return JS_FALSE; + if (!swfdec_script_ensure_stack (cx, n_items + 1)) + return JS_FALSE; + + /* items are the wrong order on the stack */ + j = - 1 - n_items; + for (i = - 2; i > j; i--, j++) { + jsval tmp = fp->sp[i]; + fp->sp[i] = fp->sp[j]; + fp->sp[j] = tmp; + } + array = JS_NewArrayObject (cx, n_items, fp->sp - n_items - 1); + if (array == NULL) + return JS_FALSE; + fp->sp -= n_items; + fp->sp[-1] = OBJECT_TO_JSVAL (array); + return JS_TRUE; +} + +static void +swfdec_action_define_function (SwfdecAsContext *cx, guint action, + const guint8 *data, guint len) +{ + const char *function_name; + guint i, n_args, size; + SwfdecBits bits; + JSFunction *fun; + SwfdecScript *script; + JSObject *scope; + gboolean has_preloads = FALSE; + guint flags = 0; + guint8 *preloads = NULL; + gboolean v2 = (action == 0x8e); + + swfdec_bits_init_data (&bits, data, len); + function_name = swfdec_bits_skip_string (&bits); + if (function_name == NULL) { + SWFDEC_ERROR ("could not parse function name"); + return JS_FALSE; + } + n_args = swfdec_bits_get_u16 (&bits); + scope = cx->fp->scopeChain; + script = cx->fp->swf; + if (script->version == 5) { + /* In Flash 5 there's only the root scope as a parent scope */ + JSObject *parent; + /* FIXME: this implementation is hacky (but it works) */ + while (JS_GetClass (scope) == &js_CallClass && (parent = JS_GetParent (cx, scope))) + scope = parent; + } + if (*function_name == '\0') { + /* anonymous function */ + fun = JS_NewFunction (cx, NULL, n_args, JSFUN_LAMBDA | JSFUN_HEAVYWEIGHT, + scope, NULL); + } else { + /* named function */ + fun = JS_NewFunction (cx, NULL, n_args, JSFUN_HEAVYWEIGHT, + scope, function_name); + } + if (fun == NULL) + return JS_FALSE; + if (v2) { + script->n_registers = swfdec_bits_get_u8 (&bits) + 1; + flags = swfdec_bits_get_u16 (&bits); + preloads = g_new0 (guint8, n_args); + } else { + script->n_registers = 5; + } + fun->nvars = script->n_registers; + for (i = 0; i < n_args; i++) { + JSAtom *atom; + const char *arg_name; + if (v2) { + guint preload = swfdec_bits_get_u8 (&bits); + if (preload && preload >= script->n_registers) { + SWFDEC_ERROR ("argument %u is preloaded into register %u out of %u", + i, preload, script->n_registers); + return JS_FALSE; + } + if (preload != 0) { + preloads[i] = preload; + swfdec_bits_skip_string (&bits); + has_preloads = TRUE; + continue; + } + } + arg_name = swfdec_bits_skip_string (&bits); + if (arg_name == NULL || *arg_name == '\0') { + SWFDEC_ERROR ("empty argument name not allowed"); + return JS_FALSE; + } + /* FIXME: check duplicate arguments */ + atom = js_Atomize (cx, arg_name, strlen (arg_name), 0); + if (atom == NULL) + return JS_FALSE; + if (!js_AddNativeProperty (cx, fun->object, (jsid) atom, + js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, + JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, + SPROP_HAS_SHORTID, i)) { + return JS_FALSE; + } + } + if (preloads && !has_preloads) { + g_free (preloads); + preloads = NULL; + } + size = swfdec_bits_get_u16 (&bits); + /* check the script can be created */ + if (script->buffer->data + script->buffer->length < cx->fp->pc + 3 + len + size) { + SWFDEC_ERROR ("size of function is too big"); + return FALSE; + } else { + /* create the script */ + const char *name = NULL; + SwfdecBuffer *buffer = swfdec_buffer_new_subbuffer (script->buffer, + cx->fp->pc + 3 + len - script->buffer->data, size); + swfdec_bits_init (&bits, buffer); + if (*function_name) { + name = function_name; + } else if (cx->fp->sp > cx->fp->spbase) { + /* This is kind of a hack that uses a feature of the Adobe compiler: + * foo = function () {} is compiled as these actions: + * Push "foo", DefineFunction, SetVariable/SetMember + * With this knowledge we can inspect the topmost stack member, since + * it will contain the name this function will soon be assigned to. + */ + if (JSVAL_IS_STRING (cx->fp->sp[-1])) + name = JS_GetStringBytes (JSVAL_TO_STRING (cx->fp->sp[-1])); + } + if (name == NULL) + name = "unnamed_function"; + script = swfdec_script_new (&bits, name, ((SwfdecScript *) cx->fp->swf)->version); + swfdec_buffer_unref (buffer); + } + if (script == NULL) { + SWFDEC_ERROR ("failed to create script"); + g_free (preloads); + return JS_FALSE; + } + if (cx->fp->constant_pool) { + script->constant_pool = swfdec_constant_pool_get_area (cx->fp->swf, + cx->fp->constant_pool); + } + script->flags = flags; + script->preloads = preloads; + fun->swf = script; + swfdec_script_add_to_player (script, JS_GetContextPrivate (cx)); + /* attach the function */ + if (*function_name == '\0') { + if (cx->fp->sp >= cx->fp->spend) { + SWFDEC_ERROR ("not enough stack space available"); + return JS_FALSE; + } + *cx->fp->sp++ = OBJECT_TO_JSVAL (fun->object); + } else { + jsval val = OBJECT_TO_JSVAL (fun->object); + if (!JS_SetProperty (cx, cx->fp->varobj, function_name, &val)) + return JS_FALSE; + } + + /* update current context */ + cx->fp->pc += 3 + len + size; + return JS_TRUE; +} + +static void +swfdec_action_bitwise (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + guint32 a, b; + double d; + + if (!JS_ValueToECMAUint32 (cx, cx->fp->sp[-1], &a) || + !JS_ValueToECMAUint32 (cx, cx->fp->sp[-2], &b)) + return JS_FALSE; + + switch (action) { + case 0x60: + d = (int) (a & b); + break; + case 0x61: + d = (int) (a | b); + break; + case 0x62: + d = (int) (a ^ b); + break; + default: + g_assert_not_reached (); + return JS_FALSE; + } + + cx->fp->sp--; + return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); +} + +static void +swfdec_action_shift (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + guint32 amount, value; + double d; + + if (!JS_ValueToECMAUint32 (cx, cx->fp->sp[-1], &amount) || + !JS_ValueToECMAUint32 (cx, cx->fp->sp[-2], &value)) + return JS_FALSE; + + amount &= 31; + switch (action) { + case 0x63: + d = value << amount; + break; + case 0x64: + d = ((gint) value) >> amount; + break; + case 0x65: + d = ((guint) value) >> amount; + break; + default: + g_assert_not_reached (); + return JS_FALSE; + } + + cx->fp->sp--; + return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); +} + +static void +swfdec_action_to_integer (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double d = swfdec_value_to_number (cx, cx->fp->sp[-1]); + + return JS_NewNumberValue (cx, (int) d, &cx->fp->sp[-1]); +} + +static void +swfdec_action_target_path (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecMovie *movie = swfdec_scriptable_from_jsval (cx, cx->fp->sp[-1], SWFDEC_TYPE_MOVIE); + + if (movie == NULL) { + cx->fp->sp[-1] = JSVAL_VOID; + } else { + char *s = swfdec_movie_get_path (movie); + JSString *string = JS_NewStringCopyZ (cx, s); + g_free (s); + if (string == NULL) + return JS_FALSE; + cx->fp->sp[-1] = STRING_TO_JSVAL (string); + } + return JS_TRUE; +} + +static void +swfdec_action_define_local (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *name; + + g_assert (cx->fp->scopeChain != NULL); + name = swfdec_js_to_string (cx, cx->fp->sp[-2]); + if (name == NULL) + return JS_FALSE; + if (!JS_SetProperty (cx, cx->fp->scopeChain, name, &cx->fp->sp[-1])) + return JS_FALSE; + cx->fp->sp -= 2; + return JS_TRUE; +} + +static void +swfdec_action_define_local2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *name; + jsval val = JSVAL_VOID; + + g_assert (cx->fp->scopeChain != NULL); + name = swfdec_js_to_string (cx, cx->fp->sp[-1]); + if (name == NULL) + return JS_FALSE; + if (!JS_SetProperty (cx, cx->fp->scopeChain, name, &val)) + return JS_FALSE; + cx->fp->sp--; + return JS_TRUE; +} + +static void +swfdec_action_return (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecScript *script = cx->fp->swf; + + cx->fp->rval = cx->fp->sp[-1]; + cx->fp->pc = script->buffer->data + script->buffer->length; + cx->fp->sp--; + return JS_TRUE; +} + +static void +swfdec_action_delete (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *name; + + cx->fp->sp -= 2; + name = swfdec_js_to_string (cx, cx->fp->sp[1]); + if (name == NULL) + return JS_FALSE; + if (!JSVAL_IS_OBJECT (cx->fp->sp[0])) + return JS_TRUE; + return JS_DeleteProperty (cx, JSVAL_TO_OBJECT (cx->fp->sp[0]), name); +} + +static void +swfdec_action_delete2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + const char *name; + JSObject *obj, *pobj; + JSProperty *prop; + JSAtom *atom; + + cx->fp->sp -= 1; + name = swfdec_js_to_string (cx, cx->fp->sp[1]); + if (name == NULL) + return JS_FALSE; + if (!(atom = js_Atomize (cx, name, strlen (name), 0)) || + !js_FindProperty (cx, (jsid) atom, &obj, &pobj, &prop)) + return JS_FALSE; + if (!pobj) + return JS_TRUE; + return JS_DeleteProperty (cx, pobj, name); +} + +static void +swfdec_action_store_register (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + if (len != 1) { + SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len); + return JS_FALSE; + } + if (!swfdec_action_has_register (cx, *data)) { + SWFDEC_ERROR ("Cannot store into register %u, not enough registers", (guint) *data); + return JS_FALSE; + } + cx->fp->vars[*data] = cx->fp->sp[-1]; + return JS_TRUE; +} + +static void +swfdec_action_modulo_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double x, y; + + x = swfdec_value_to_number (cx, cx->fp->sp[-1]); + y = swfdec_value_to_number (cx, cx->fp->sp[-2]); + cx->fp->sp--; + errno = 0; + x = fmod (x, y); + if (errno != 0) { + cx->fp->sp[-1] = DOUBLE_TO_JSVAL (cx->runtime->jsNaN); + return JS_TRUE; + } else { + return JS_NewNumberValue (cx, x, &cx->fp->sp[-1]); + } +} + +static void +swfdec_action_modulo_7 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double x, y; + + if (!swfdec_value_to_number_7 (cx, cx->fp->sp[-1], &x) || + !swfdec_value_to_number_7 (cx, cx->fp->sp[-2], &y)) + return JS_FALSE; + cx->fp->sp--; + errno = 0; + x = fmod (x, y); + if (errno != 0) { + cx->fp->sp[-1] = DOUBLE_TO_JSVAL (cx->runtime->jsNaN); + return JS_TRUE; + } else { + return JS_NewNumberValue (cx, x, &cx->fp->sp[-1]); + } +} + +static void +swfdec_action_swap (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval tmp = cx->fp->sp[-2]; + cx->fp->sp[-2] = cx->fp->sp[-1]; + cx->fp->sp[-1] = tmp; + return JS_TRUE; +} + +static void +swfdec_action_to_number (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + double d; + if (!JS_ValueToNumber (cx, cx->fp->sp[-1], &d)) + return JS_FALSE; + return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); +} + +static void +swfdec_action_to_string (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSString *s; + s = JS_ValueToString(cx, cx->fp->sp[-1]); + if (!s) + return JS_FALSE; + cx->fp->sp[-1] = STRING_TO_JSVAL (s); + return JS_TRUE; +} + +static void +swfdec_action_type_of (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval val; + const char *type; + JSString *string; + + val = cx->fp->sp[-1]; + if (JSVAL_IS_NUMBER (val)) { + type = "number"; + } else if (JSVAL_IS_BOOLEAN (val)) { + type = "boolean"; + } else if (JSVAL_IS_STRING (val)) { + type = "string"; + } else if (JSVAL_IS_VOID (val)) { + type = "undefined"; + } else if (JSVAL_IS_NULL (val)) { + type = "null"; + } else if (JSVAL_IS_OBJECT (val)) { + JSObject *obj = JSVAL_TO_OBJECT (val); + if (swfdec_js_is_movieclip (cx, obj)) { + type = "movieclip"; + } else if (JS_ObjectIsFunction (cx, obj)) { + type = "function"; + } else { + type = "object"; + } + } else { + g_assert_not_reached (); + return JS_FALSE; + } + /* can't use InternString here because of case sensitivity issues */ + string = JS_NewStringCopyZ (cx, type); + if (string == NULL) + return JS_FALSE; + cx->fp->sp[-1] = STRING_TO_JSVAL (string); + return JS_TRUE; +} + +static void +swfdec_action_get_time (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + SwfdecPlayer *player = JS_GetContextPrivate (cx); + + *cx->fp->sp++ = INT_TO_JSVAL ((int) SWFDEC_TICKS_TO_MSECS (player->time)); + return JS_TRUE; +} + +static void +swfdec_action_extends (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + jsval superclass, subclass, proto; + JSObject *prototype; + + superclass = cx->fp->sp[-1]; + subclass = cx->fp->sp[-2]; + cx->fp->sp -= 2; + if (!JSVAL_IS_OBJECT (superclass) || superclass == JSVAL_NULL || + !JSVAL_IS_OBJECT (subclass) || subclass == JSVAL_NULL) { + SWFDEC_ERROR ("superclass or subclass aren't objects"); + return JS_TRUE; + } + if (!JS_GetProperty (cx, JSVAL_TO_OBJECT (superclass), "prototype", &proto) || + !JSVAL_IS_OBJECT (proto)) + return JS_FALSE; + prototype = JS_NewObject (cx, NULL, JSVAL_TO_OBJECT (proto), NULL); + if (prototype == NULL) + return JS_FALSE; + proto = OBJECT_TO_JSVAL (prototype); + if (!JS_SetProperty (cx, prototype, "__constructor__", &superclass) || + !JS_SetProperty (cx, JSVAL_TO_OBJECT (subclass), "prototype", &proto)) + return JS_FALSE; + return JS_TRUE; +} + +static void +swfdec_action_enumerate2 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + JSObject *obj; + JSIdArray *array; + guint i; + + if (!JSVAL_IS_OBJECT (cx->fp->sp[-1]) || cx->fp->sp[-1] == JSVAL_NULL) { + SWFDEC_ERROR ("Enumerate2 called without an object"); + cx->fp->sp[-1] = JSVAL_NULL; + return JS_TRUE; + } + obj = JSVAL_TO_OBJECT (cx->fp->sp[-1]); + cx->fp->sp[-1] = JSVAL_NULL; + array = JS_Enumerate (cx, obj); + if (!array) + return JS_FALSE; + if ((guint) (cx->fp->spend - cx->fp->sp) < array->length) { + SWFDEC_ERROR ("FIXME: not enough stack space, need %u, got %td", + array->length, cx->fp->spend - cx->fp->sp); + JS_DestroyIdArray (cx, array); + return JS_FALSE; + } + for (i = 0; i < array->length; i++) { + if (!JS_IdToValue (cx, array->vector[i], cx->fp->sp++)) { + JS_DestroyIdArray (cx, array); + return JS_FALSE; + } + } + JS_DestroyIdArray (cx, array); + return JS_TRUE; +} + +static void +swfdec_action_logical_5 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + void l, r; + + l = swfdec_value_to_boolean_5 (cx, cx->fp->sp[-1]); + r = swfdec_value_to_boolean_5 (cx, cx->fp->sp[-2]); + + cx->fp->sp--; + if (action == 0x10) + cx->fp->sp[-1] = l && r ? JSVAL_TRUE : JSVAL_FALSE; + else + cx->fp->sp[-1] = l || r ? JSVAL_TRUE : JSVAL_FALSE; + return JS_TRUE; +} + +static void +swfdec_action_logical_7 (SwfdecAsContext *cx, guint action, const guint8 *data, guint len) +{ + void l, r; + + l = swfdec_value_to_boolean_7 (cx, cx->fp->sp[-1]); + r = swfdec_value_to_boolean_7 (cx, cx->fp->sp[-2]); + + cx->fp->sp--; + if (action == 0x10) + cx->fp->sp[-1] = l && r ? JSVAL_TRUE : JSVAL_FALSE; + else + cx->fp->sp[-1] = l || r ? JSVAL_TRUE : JSVAL_FALSE; + return JS_TRUE; +} + +/*** PRINT FUNCTIONS ***/ + +static char * +swfdec_action_print_store_register (guint action, const guint8 *data, guint len) +{ + if (len != 1) { + SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len); + return NULL; + } + return g_strdup_printf ("StoreRegister %u", (guint) *data); +} + +static char * +swfdec_action_print_set_target (guint action, const guint8 *data, guint len) +{ + if (!memchr (data, 0, len)) { + SWFDEC_ERROR ("SetTarget action does not specify a string"); + return JS_FALSE; + } + return g_strconcat ("SetTarget ", data, NULL); +} + +static char * +swfdec_action_print_define_function (guint action, const guint8 *data, guint len) +{ + SwfdecBits bits; + GString *string; + const char *function_name; + guint i, n_args, size; + gboolean v2 = (action == 0x8e); + + string = g_string_new (v2 ? "DefineFunction2 " : "DefineFunction "); + swfdec_bits_init_data (&bits, data, len); + function_name = swfdec_bits_get_string (&bits); + if (function_name == NULL) { + SWFDEC_ERROR ("could not parse function name"); + g_string_free (string, TRUE); + return NULL; + } + if (*function_name) { + g_string_append (string, function_name); + g_string_append_c (string, ' '); + } + n_args = swfdec_bits_get_u16 (&bits); + g_string_append_c (string, '('); + if (v2) { + /* n_regs = */ swfdec_bits_get_u8 (&bits); + /* flags = */ swfdec_bits_get_u16 (&bits); + } + + for (i = 0; i < n_args; i++) { + guint preload; + const char *arg_name; + if (v2) + preload = swfdec_bits_get_u8 (&bits); + else + preload = 0; + arg_name = swfdec_bits_get_string (&bits); + if (preload == 0 && (arg_name == NULL || *arg_name == '\0')) { + SWFDEC_ERROR ("empty argument name not allowed"); + g_string_free (string, TRUE); + return NULL; + } + if (i) + g_string_append (string, ", "); + if (preload) + g_string_append_printf (string, "PRELOAD %u", preload); + else + g_string_append (string, arg_name); + } + g_string_append_c (string, ')'); + size = swfdec_bits_get_u16 (&bits); + g_string_append_printf (string, " %u", size); + return g_string_free (string, FALSE); +} + +static char * +swfdec_action_print_get_url2 (guint action, const guint8 *data, guint len) +{ + guint method; + + if (len != 1) { + SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len); + return NULL; + } + method = data[0] >> 6; + if (method == 3) { + SWFDEC_ERROR ("GetURL method 3 invalid"); + method = 0; + } + if (method) { + SWFDEC_ERROR ("FIXME: implement encoding variables using %s", method == 1 ? "GET" : "POST"); + } + return g_strdup_printf ("GetURL2%s%s%s", method == 0 ? "" : (method == 1 ? " GET" : " POST"), + data[0] & 2 ? " LoadTarget" : "", data[0] & 1 ? " LoadVariables" : ""); +} + +static char * +swfdec_action_print_get_url (guint action, const guint8 *data, guint len) +{ + SwfdecBits bits; + const char *url, *target; + + swfdec_bits_init_data (&bits, data, len); + url = swfdec_bits_skip_string (&bits); + target = swfdec_bits_skip_string (&bits); + if (swfdec_bits_left (&bits)) { + SWFDEC_WARNING ("leftover bytes in GetURL action"); + } + return g_strdup_printf ("GetURL %s %s", url, target); +} + +static char * +swfdec_action_print_if (guint action, const guint8 *data, guint len) +{ + if (len != 2) { + SWFDEC_ERROR ("If action length invalid (is %u, should be 2", len); + return NULL; + } + return g_strdup_printf ("If %d", GINT16_FROM_LE (*((gint16*) data))); +} + +static char * +swfdec_action_print_jump (guint action, const guint8 *data, guint len) +{ + if (len != 2) { + SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len); + return NULL; + } + return g_strdup_printf ("Jump %d", GINT16_FROM_LE (*((gint16*) data))); +} + +static char * +swfdec_action_print_push (guint action, const guint8 *data, guint len) +{ + gboolean first = TRUE; + SwfdecBits bits; + GString *string = g_string_new ("Push"); + + swfdec_bits_init_data (&bits, data, len); + while (swfdec_bits_left (&bits)) { + guint type = swfdec_bits_get_u8 (&bits); + if (first) + g_string_append (string, " "); + else + g_string_append (string, ", "); + first = FALSE; + switch (type) { + case 0: /* string */ + { + const char *s = swfdec_bits_skip_string (&bits); + if (!s) { + g_string_free (string, TRUE); + return NULL; + } + g_string_append_c (string, '"'); + g_string_append (string, s); + g_string_append_c (string, '"'); + break; + } + case 1: /* float */ + g_string_append_printf (string, "%g", swfdec_bits_get_float (&bits)); + break; + case 2: /* null */ + g_string_append (string, "null"); + break; + case 3: /* undefined */ + g_string_append (string, "void"); + break; + case 4: /* register */ + g_string_append_printf (string, "Register %u", swfdec_bits_get_u8 (&bits)); + break; + case 5: /* boolean */ + g_string_append (string, swfdec_bits_get_u8 (&bits) ? "True" : "False"); + break; + case 6: /* double */ + g_string_append_printf (string, "%g", swfdec_bits_get_double (&bits)); + break; + case 7: /* 32bit int */ + g_string_append_printf (string, "%d", swfdec_bits_get_u32 (&bits)); + break; + case 8: /* 8bit ConstantPool address */ + g_string_append_printf (string, "Pool %u", swfdec_bits_get_u8 (&bits)); + break; + case 9: /* 16bit ConstantPool address */ + g_string_append_printf (string, "Pool %u", swfdec_bits_get_u16 (&bits)); + break; + default: + SWFDEC_ERROR ("Push: type %u not implemented", type); + return JS_FALSE; + } + } + return g_string_free (string, FALSE); +} + +/* NB: constant pool actions are special in that they are called at init time */ +static char * +swfdec_action_print_constant_pool (guint action, const guint8 *data, guint len) +{ + guint i; + GString *string; + SwfdecConstantPool *pool; + + pool = swfdec_constant_pool_new_from_action (data, len); + if (pool == NULL) + return JS_FALSE; + string = g_string_new ("ConstantPool"); + for (i = 0; i < swfdec_constant_pool_size (pool); i++) { + g_string_append (string, i ? ", " : " "); + g_string_append (string, swfdec_constant_pool_get (pool, i)); + g_string_append_printf (string, " (%u)", i); + } + return g_string_free (string, FALSE); +} + +static char * +swfdec_action_print_wait_for_frame2 (guint action, const guint8 *data, guint len) +{ + if (len != 1) { + SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data"); + return NULL; + } + return g_strdup_printf ("WaitForFrame2 %u", (guint) *data); +} + +static char * +swfdec_action_print_goto_frame2 (guint action, const guint8 *data, guint len) +{ + gboolean play, bias; + SwfdecBits bits; + + swfdec_bits_init_data (&bits, data, len); + if (swfdec_bits_getbits (&bits, 6)) { + SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0"); + } + bias = swfdec_bits_getbit (&bits); + play = swfdec_bits_getbit (&bits); + if (bias) { + return g_strdup_printf ("GotoFrame2 %s +%u", play ? "play" : "stop", + swfdec_bits_get_u16 (&bits)); + } else { + return g_strdup_printf ("GotoFrame2 %s", play ? "play" : "stop"); + } +} +#endif + +static char * +swfdec_action_print_goto_frame (guint action, const guint8 *data, guint len) +{ + guint frame; + + if (len != 2) + return NULL; + + frame = GUINT16_FROM_LE (*((guint16 *) data)); + return g_strdup_printf ("GotoFrame %u", frame); +} + +static char * +swfdec_action_print_goto_label (guint action, const guint8 *data, guint len) +{ + if (!memchr (data, 0, len)) { + SWFDEC_ERROR ("GotoLabel action does not specify a string"); + return NULL; + } + + return g_strdup_printf ("GotoLabel %s", data); +} + +static char * +swfdec_action_print_wait_for_frame (guint action, const guint8 *data, guint len) +{ + guint frame, jump; + + if (len != 3) + return NULL; + + frame = GUINT16_FROM_LE (*((guint16 *) data)); + jump = data[2]; + return g_strdup_printf ("WaitForFrame %u %u", frame, jump); +} + +/*** BIG FUNCTION TABLE ***/ + +const SwfdecActionSpec swfdec_as_actions[256] = { + /* version 3 */ + [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 } }, + [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 } }, + [SWFDEC_AS_ACTION_PLAY] = { "Play", NULL, 0, 0, { swfdec_action_play, swfdec_action_play, swfdec_action_play, swfdec_action_play, swfdec_action_play } }, + [SWFDEC_AS_ACTION_STOP] = { "Stop", NULL, 0, 0, { swfdec_action_stop, swfdec_action_stop, swfdec_action_stop, swfdec_action_stop, swfdec_action_stop } }, +#if 0 + [0x08] = { "ToggleQuality", NULL }, + [0x09] = { "StopSounds", NULL, 0, 0, { swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds } }, + /* version 4 */ + [0x0a] = { "Add", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } }, + [0x0b] = { "Subtract", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } }, + [0x0c] = { "Multiply", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } }, + [0x0d] = { "Divide", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } }, + [0x0e] = { "Equals", NULL, 2, 1, { NULL, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare } }, + [0x0f] = { "Less", NULL, 2, 1, { NULL, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare } }, + [0x10] = { "And", NULL, 2, 1, { NULL, /* FIXME */NULL, swfdec_action_logical_5, swfdec_action_logical_5, swfdec_action_logical_7 } }, + [0x11] = { "Or", NULL, 2, 1, { NULL, /* FIXME */NULL, swfdec_action_logical_5, swfdec_action_logical_5, swfdec_action_logical_7 } }, + [0x12] = { "Not", NULL, 1, 1, { NULL, swfdec_action_not_4, swfdec_action_not_5, swfdec_action_not_5, swfdec_action_not_5 } }, + [0x13] = { "StringEquals", NULL }, + [0x14] = { "StringLength", NULL }, + [0x15] = { "StringExtract", NULL }, + [0x17] = { "Pop", NULL, 1, 0, { NULL, swfdec_action_pop, swfdec_action_pop, swfdec_action_pop, swfdec_action_pop } }, + [0x18] = { "ToInteger", NULL, 1, 1, { NULL, swfdec_action_to_integer, swfdec_action_to_integer, swfdec_action_to_integer, swfdec_action_to_integer } }, + [0x1c] = { "GetVariable", NULL, 1, 1, { NULL, swfdec_action_get_variable, swfdec_action_get_variable, swfdec_action_get_variable, swfdec_action_get_variable } }, + [0x1d] = { "SetVariable", NULL, 2, 0, { NULL, swfdec_action_set_variable, swfdec_action_set_variable, swfdec_action_set_variable, swfdec_action_set_variable } }, + [0x20] = { "SetTarget2", NULL, 1, 0, { swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2 } }, + [0x21] = { "StringAdd", NULL, 2, 1, { NULL, swfdec_action_string_add, swfdec_action_string_add, swfdec_action_string_add, swfdec_action_string_add } }, + [0x22] = { "GetProperty", NULL, 2, 1, { NULL, swfdec_action_get_property, swfdec_action_get_property, swfdec_action_get_property, swfdec_action_get_property } }, + [0x23] = { "SetProperty", NULL, 3, 0, { NULL, swfdec_action_set_property, swfdec_action_set_property, swfdec_action_set_property, swfdec_action_set_property } }, + [0x24] = { "CloneSprite", NULL }, + [0x25] = { "RemoveSprite", NULL }, + [0x26] = { "Trace", NULL, 1, 0, { NULL, swfdec_action_trace, swfdec_action_trace, swfdec_action_trace, swfdec_action_trace } }, + [0x27] = { "StartDrag", NULL, -1, 0, { NULL, swfdec_action_start_drag, swfdec_action_start_drag, swfdec_action_start_drag, swfdec_action_start_drag } }, + [0x28] = { "EndDrag", NULL, 0, 0, { NULL, swfdec_action_end_drag, swfdec_action_end_drag, swfdec_action_end_drag, swfdec_action_end_drag } }, + [0x29] = { "StringLess", NULL }, + /* version 7 */ + [0x2a] = { "Throw", NULL }, + [0x2b] = { "Cast", NULL }, + [0x2c] = { "Implements", NULL }, + /* version 4 */ + [0x30] = { "RandomNumber", NULL, 1, 1, { NULL, swfdec_action_random_number, swfdec_action_random_number, swfdec_action_random_number, swfdec_action_random_number } }, + [0x31] = { "MBStringLength", NULL }, + [0x32] = { "CharToAscii", NULL }, + [0x33] = { "AsciiToChar", NULL }, + [0x34] = { "GetTime", NULL, 0, 1, { NULL, swfdec_action_get_time, swfdec_action_get_time, swfdec_action_get_time, swfdec_action_get_time } }, + [0x35] = { "MBStringExtract", NULL }, + [0x36] = { "MBCharToAscii", NULL }, + [0x37] = { "MVAsciiToChar", NULL }, + /* version 5 */ + [0x3a] = { "Delete", NULL, 2, 0, { NULL, NULL, swfdec_action_delete, swfdec_action_delete, swfdec_action_delete } }, + [0x3b] = { "Delete2", NULL, 1, 0, { NULL, NULL, swfdec_action_delete2, swfdec_action_delete2, swfdec_action_delete2 } }, + [0x3c] = { "DefineLocal", NULL, 2, 0, { NULL, NULL, swfdec_action_define_local, swfdec_action_define_local, swfdec_action_define_local } }, + [0x3d] = { "CallFunction", NULL, -1, 1, { NULL, NULL, swfdec_action_call_function, swfdec_action_call_function, swfdec_action_call_function } }, + [0x3e] = { "Return", NULL, 1, 0, { NULL, NULL, swfdec_action_return, swfdec_action_return, swfdec_action_return } }, + [0x3f] = { "Modulo", NULL, 2, 1, { NULL, NULL, swfdec_action_modulo_5, swfdec_action_modulo_5, swfdec_action_modulo_7 } }, + [0x40] = { "NewObject", NULL, -1, 1, { NULL, NULL, swfdec_action_new_object, swfdec_action_new_object, swfdec_action_new_object } }, + [0x41] = { "DefineLocal2", NULL, 1, 0, { NULL, NULL, swfdec_action_define_local2, swfdec_action_define_local2, swfdec_action_define_local2 } }, + [0x42] = { "InitArray", NULL, -1, 1, { NULL, NULL, swfdec_action_init_array, swfdec_action_init_array, swfdec_action_init_array } }, + [0x43] = { "InitObject", NULL, -1, 1, { NULL, NULL, swfdec_action_init_object, swfdec_action_init_object, swfdec_action_init_object } }, + [0x44] = { "TypeOf", NULL, 1, 1, { NULL, NULL, swfdec_action_type_of, swfdec_action_type_of, swfdec_action_type_of } }, + [0x45] = { "TargetPath", NULL, 1, 1, { NULL, NULL, swfdec_action_target_path, swfdec_action_target_path, swfdec_action_target_path } }, + [0x46] = { "Enumerate", NULL }, + [0x47] = { "Add2", NULL, 2, 1, { NULL, NULL, swfdec_action_add2_5, swfdec_action_add2_5, swfdec_action_add2_7 } }, + [0x48] = { "Less2", NULL, 2, 1, { NULL, NULL, swfdec_action_new_comparison_6, swfdec_action_new_comparison_6, swfdec_action_new_comparison_7 } }, + [0x49] = { "Equals2", NULL, 2, 1, { NULL, NULL, swfdec_action_equals2, swfdec_action_equals2, swfdec_action_equals2 } }, + [0x4a] = { "ToNumber", NULL, 1, 1, { NULL, NULL, swfdec_action_to_number, swfdec_action_to_number, swfdec_action_to_number } }, + [0x4b] = { "ToString", NULL, 1, 1, { NULL, NULL, swfdec_action_to_string, swfdec_action_to_string, swfdec_action_to_string } }, + [0x4c] = { "PushDuplicate", NULL, 1, 2, { NULL, NULL, swfdec_action_push_duplicate, swfdec_action_push_duplicate, swfdec_action_push_duplicate } }, + [0x4d] = { "Swap", NULL, 2, 2, { NULL, NULL, swfdec_action_swap, swfdec_action_swap, swfdec_action_swap } }, + [0x4e] = { "GetMember", NULL, 2, 1, { NULL, swfdec_action_get_member, swfdec_action_get_member, swfdec_action_get_member, swfdec_action_get_member } }, + [0x4f] = { "SetMember", NULL, 3, 0, { NULL, swfdec_action_set_member, swfdec_action_set_member, swfdec_action_set_member, swfdec_action_set_member } }, + [0x50] = { "Increment", NULL, 1, 1, { NULL, NULL, swfdec_action_increment, swfdec_action_increment, swfdec_action_increment } }, + [0x51] = { "Decrement", NULL, 1, 1, { NULL, NULL, swfdec_action_decrement, swfdec_action_decrement, swfdec_action_decrement } }, + [0x52] = { "CallMethod", NULL, -1, 1, { NULL, NULL, swfdec_action_call_method, swfdec_action_call_method, swfdec_action_call_method } }, + [0x53] = { "NewMethod", NULL, -1, 1, { NULL, NULL, swfdec_action_new_method, swfdec_action_new_method, swfdec_action_new_method } }, + /* version 6 */ + [0x54] = { "InstanceOf", NULL }, + [0x55] = { "Enumerate2", NULL, 1, -1, { NULL, NULL, NULL, swfdec_action_enumerate2, swfdec_action_enumerate2 } }, + /* version 5 */ + [0x60] = { "BitAnd", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } }, + [0x61] = { "BitOr", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } }, + [0x62] = { "BitXor", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } }, + [0x63] = { "BitLShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } }, + [0x64] = { "BitRShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } }, + [0x65] = { "BitURShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } }, + /* version 6 */ + [0x66] = { "StrictEquals", NULL }, + [0x67] = { "Greater", NULL, 2, 1, { NULL, NULL, NULL, swfdec_action_new_comparison_6, swfdec_action_new_comparison_7 } }, + [0x68] = { "StringGreater", NULL }, + /* version 7 */ + [0x69] = { "Extends", NULL, 2, 0, { NULL, NULL, NULL, NULL, swfdec_action_extends } }, + + /* version 3 */ +#endif + [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 } }, +#if 0 + [0x83] = { "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 } }, + /* version 5 */ + [0x87] = { "StoreRegister", swfdec_action_print_store_register, 1, 1, { NULL, NULL, swfdec_action_store_register, swfdec_action_store_register, swfdec_action_store_register } }, + [0x88] = { "ConstantPool", swfdec_action_print_constant_pool, 0, 0, { NULL, NULL, swfdec_action_constant_pool, swfdec_action_constant_pool, swfdec_action_constant_pool } }, + /* version 3 */ +#endif + [0x8a] = { "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 } }, +#if 0 + [0x8b] = { "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 } }, +#endif + [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 } }, +#if 0 + /* version 4 */ + [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 } }, + /* version 7 */ + [0x8e] = { "DefineFunction2", swfdec_action_print_define_function, 0, -1, { NULL, NULL, NULL, swfdec_action_define_function, swfdec_action_define_function } }, + [0x8f] = { "Try", NULL }, + /* version 5 */ + [0x94] = { "With", NULL }, + /* version 4 */ + [0x96] = { "Push", swfdec_action_print_push, 0, -1, { NULL, swfdec_action_push, swfdec_action_push, swfdec_action_push, swfdec_action_push } }, + [0x99] = { "Jump", swfdec_action_print_jump, 0, 0, { NULL, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump } }, + [0x9a] = { "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 } }, + /* version 5 */ + [0x9b] = { "DefineFunction", swfdec_action_print_define_function, 0, -1, { NULL, NULL, swfdec_action_define_function, swfdec_action_define_function, swfdec_action_define_function } }, + /* version 4 */ + [0x9d] = { "If", swfdec_action_print_if, 1, 0, { NULL, swfdec_action_if, swfdec_action_if, swfdec_action_if, swfdec_action_if } }, + [0x9e] = { "Call", NULL }, + [0x9f] = { "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 } } +#endif +}; + diff --git a/libswfdec/swfdec_as_interpret.h b/libswfdec/swfdec_as_interpret.h new file mode 100644 index 0000000..b388db0 --- /dev/null +++ b/libswfdec/swfdec_as_interpret.h @@ -0,0 +1,45 @@ +/* SwfdecAs + * Copyright (C) 2007 Benjamin Otte <otte@gnome.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef _SWFDEC_AS_INTERPRET_H_ +#define _SWFDEC_AS_INTERPRET_H_ + +#include <libswfdec/swfdec_as_types.h> + +G_BEGIN_DECLS + +/* defines minimum and maximum versions for which we have seperate scripts */ +#define SWFDEC_AS_MIN_SCRIPT_VERSION 3 +#define SWFDEC_AS_MAX_SCRIPT_VERSION 7 +#define SWFDEC_AS_EXTRACT_SCRIPT_VERSION(v) MIN ((v) - SWFDEC_AS_MIN_SCRIPT_VERSION, SWFDEC_AS_MAX_SCRIPT_VERSION - SWFDEC_AS_MIN_SCRIPT_VERSION) + +typedef void (* SwfdecActionExec) (SwfdecAsContext *cx, guint action, const guint8 *data, guint len); +typedef struct { + const char * name; /* name identifying the action */ + char * (* print) (guint action, const guint8 *data, guint len); + int remove; /* values removed from stack or -1 for dynamic */ + int add; /* values added to the stack or -1 for dynamic */ + SwfdecActionExec exec[SWFDEC_AS_MAX_SCRIPT_VERSION - SWFDEC_AS_MIN_SCRIPT_VERSION + 1]; + /* array is for version 3, 4, 5, 6, 7+ */ +} SwfdecActionSpec; + +extern const SwfdecActionSpec swfdec_as_actions[256]; + +G_END_DECLS +#endif diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c index a537953..293a48e 100644 --- a/libswfdec/swfdec_script.c +++ b/libswfdec/swfdec_script.c @@ -23,6 +23,7 @@ #include "swfdec_script.h" #include "swfdec_as_context.h" +#include "swfdec_as_interpret.h" #include "swfdec_debug.h" #include "swfdec_debugger.h" @@ -148,2445 +149,10 @@ swfdec_script_add_to_player (SwfdecScrip } } -#define swfdec_action_has_register(cx, i) \ - ((i) < ((SwfdecScript *) (cx)->fp->swf)->n_registers) - -#if 0 -static SwfdecMovie * -swfdec_action_get_target (JSContext *cx) -{ - JSObject *object = cx->fp->scopeChain; - /* this whole function needs a big FIXME */ - if (JS_GetClass (object) == &js_WithClass) - object = JS_GetPrototype (cx, object); - return swfdec_scriptable_from_jsval (cx, OBJECT_TO_JSVAL (object), SWFDEC_TYPE_MOVIE); -} - -static JSBool -swfdec_action_push_string (JSContext *cx, const char *s) -{ - JSString *string = JS_NewStringCopyZ (cx, s); - if (string == NULL) - return JS_FALSE; - *cx->fp->sp++ = STRING_TO_JSVAL (string); - return JS_TRUE; -} - -static JSBool -swfdec_value_to_boolean_5 (JSContext *cx, jsval val) -{ - if (JSVAL_IS_BOOLEAN (val)) { - return JSVAL_TO_BOOLEAN (val); - } else if (JSVAL_IS_INT (val)) { - return JSVAL_TO_INT (val) != 0; - } else if (JSVAL_IS_DOUBLE (val)) { - double d = *JSVAL_TO_DOUBLE (val); - return d != 0.0 && !isnan (d); - } else if (JSVAL_IS_STRING (val)) { - double d; - if (!JS_ValueToNumber (cx, val, &d)) - return 0; - return d != 0.0 && !isnan (d); - } else if (JSVAL_IS_NULL (val)) { - return JS_FALSE; - } else if (JSVAL_IS_VOID (val)) { - return JS_FALSE; - } else if (JSVAL_IS_OBJECT (val)) { - return JS_TRUE; - } - g_assert_not_reached (); - return JS_FALSE; -} - -static JSBool -swfdec_value_to_boolean_7 (JSContext *cx, jsval val) -{ - if (JSVAL_IS_BOOLEAN (val)) { - return JSVAL_TO_BOOLEAN (val); - } else if (JSVAL_IS_INT (val)) { - return JSVAL_TO_INT (val) != 0; - } else if (JSVAL_IS_DOUBLE (val)) { - double d = *JSVAL_TO_DOUBLE (val); - return d != 0.0 && !isnan (d); - } else if (JSVAL_IS_STRING (val)) { - return JS_GetStringLength (JSVAL_TO_STRING (val)) > 0; - } else if (JSVAL_IS_NULL (val)) { - return JS_FALSE; - } else if (JSVAL_IS_VOID (val)) { - return JS_FALSE; - } else if (JSVAL_IS_OBJECT (val)) { - return JS_TRUE; - } - g_assert_not_reached (); - return JS_FALSE; -} - -static double -swfdec_value_to_number (JSContext *cx, jsval val) -{ - if (JSVAL_IS_INT (val)) { - return JSVAL_TO_INT (val); - } else if (JSVAL_IS_DOUBLE (val)) { - return *JSVAL_TO_DOUBLE (val); - } else if (JSVAL_IS_BOOLEAN (val)) { - return JSVAL_TO_BOOLEAN (val); - } else if (JSVAL_IS_STRING (val)) { - double d; - if (!JS_ValueToNumber (cx, val, &d)) - return 0; - return d; - } else if (JSVAL_IS_OBJECT(val) && (((SwfdecScript *) cx->fp->swf)->version >= 6)) { - /* Checking for version 6 is completely wrong, but a lot of the testsuite - * depends on it (oops). - * The code calls the valueOf function and returns 0 if no such function exists. - */ - return JSVAL_IS_NULL (val) ? 0 : *cx->runtime->jsNaN; - } else { - return 0; - } -} - -static JSBool -swfdec_value_to_number_7 (JSContext *cx, jsval val, double *d) -{ - if (JSVAL_IS_OBJECT (val)) { - *d = *cx->runtime->jsNaN; - return JS_TRUE; - } else if (JSVAL_IS_STRING (val) && - JS_GetStringLength (JSVAL_TO_STRING (val)) == 0) { - *d = *cx->runtime->jsNaN; - return JS_TRUE; - } else { - return JS_ValueToNumber (cx, val, d); - } -} - -/*** ALL THE ACTION IS HERE ***/ - -static JSBool -swfdec_action_stop (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie = swfdec_action_get_target (cx); - if (movie) - movie->stopped = TRUE; - else - SWFDEC_ERROR ("no movie to stop"); - return JS_TRUE; -} - -static JSBool -swfdec_action_play (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie = swfdec_action_get_target (cx); - if (movie) - movie->stopped = FALSE; - else - SWFDEC_ERROR ("no movie to play"); - return JS_TRUE; -} - -static JSBool -swfdec_action_next_frame (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie = swfdec_action_get_target (cx); - if (movie) { - if (movie->frame + 1 < movie->n_frames) { - swfdec_movie_goto (movie, movie->frame + 1); - } else { - SWFDEC_INFO ("can't execute nextFrame, already at last frame"); - } - } else { - SWFDEC_ERROR ("no movie to nextFrame on"); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_previous_frame (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie = swfdec_action_get_target (cx); - if (movie) { - if (movie->frame > 0) { - swfdec_movie_goto (movie, movie->frame - 1); - } else { - SWFDEC_INFO ("can't execute previousFrame, already at first frame"); - } - } else { - SWFDEC_ERROR ("no movie to previousFrame on"); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_goto_frame (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie = swfdec_action_get_target (cx); - guint frame; - - if (len != 2) { - SWFDEC_ERROR ("GotoFrame action length invalid (is %u, should be 2", len); - return JS_FALSE; - } - frame = GUINT16_FROM_LE (*((guint16 *) data)); - if (movie) { - swfdec_movie_goto (movie, frame); - movie->stopped = TRUE; - } else { - SWFDEC_ERROR ("no movie to goto on"); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_goto_label (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie = swfdec_action_get_target (cx); - - if (!memchr (data, 0, len)) { - SWFDEC_ERROR ("GotoLabel action does not specify a string"); - return JS_FALSE; - } - - if (SWFDEC_IS_SPRITE_MOVIE (movie)) { - int frame = swfdec_sprite_get_frame (SWFDEC_SPRITE_MOVIE (movie)->sprite, (const char *) data); - if (frame == -1) - return JS_TRUE; - swfdec_movie_goto (movie, frame); - movie->stopped = TRUE; - } else { - SWFDEC_ERROR ("no movie to goto on"); - } - return JS_TRUE; -} - -static int -swfdec_value_to_frame (JSContext *cx, SwfdecMovie *movie, jsval val) -{ - int frame; - - if (JSVAL_IS_STRING (val)) { - const char *name = swfdec_js_to_string (cx, val); - char *end; - if (name == NULL || - !SWFDEC_IS_SPRITE_MOVIE (movie)) - return -1; - if (strchr (name, ':')) { - SWFDEC_ERROR ("FIXME: handle targets"); - } - /* treat valid encoded numbers as numbers, otherwise assume it's a frame label */ - frame = strtol (name, &end, 0); - if (*end != '\0') - frame = swfdec_sprite_get_frame (SWFDEC_SPRITE_MOVIE (movie)->sprite, name); - else - frame--; - } else if (JSVAL_IS_INT (val)) { - return JSVAL_TO_INT (val) - 1; - } else if (JSVAL_IS_DOUBLE (val)) { - return (int) *JSVAL_TO_DOUBLE (val) - 1; - } else { - /* FIXME: how do we treat undefined etc? */ - frame = -1; - } - return frame; -} - -static JSBool -swfdec_action_goto_frame2 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecBits bits; - guint bias; - gboolean play; - jsval val; - SwfdecMovie *movie; - - swfdec_bits_init_data (&bits, data, len); - if (swfdec_bits_getbits (&bits, 6)) { - SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0"); - } - bias = swfdec_bits_getbit (&bits); - play = swfdec_bits_getbit (&bits); - if (bias) { - bias = swfdec_bits_get_u16 (&bits); - } - val = cx->fp->sp[-1]; - cx->fp->sp--; - movie = swfdec_action_get_target (cx); - /* now set it */ - if (movie) { - int frame = swfdec_value_to_frame (cx, movie, val); - if (frame < 0) - return JS_TRUE; - frame += bias; - frame = CLAMP (frame, 0, (int) movie->n_frames - 1); - swfdec_movie_goto (movie, frame); - movie->stopped = !play; - } else { - SWFDEC_ERROR ("no movie to GotoFrame2 on"); - } - return JS_TRUE; -} - -static void -swfdec_script_skip_actions (JSContext *cx, guint jump) -{ - SwfdecScript *script = cx->fp->swf; - guint8 *pc = cx->fp->pc; - guint8 *endpc = script->buffer->data + script->buffer->length; - - /* jump instructions */ - g_assert (script); - do { - if (pc >= endpc) - break; - if (*pc & 0x80) { - if (pc + 2 >= endpc) - break; - pc += 3 + GUINT16_FROM_LE (*((guint16 *) (pc + 1))); - } else { - pc++; - } - } while (jump-- > 0); - cx->fp->pc = pc; -} - -static JSBool -swfdec_action_wait_for_frame2 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval val; - SwfdecMovie *movie; - - if (len != 1) { - SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data"); - return JS_FALSE; - } - val = cx->fp->sp[-1]; - cx->fp->sp--; - movie = swfdec_action_get_target (cx); - if (movie) { - int frame = swfdec_value_to_frame (cx, movie, val); - guint jump = data[2]; - guint loaded; - if (frame < 0) - return JS_TRUE; - if (SWFDEC_IS_ROOT_MOVIE (movie)) { - SwfdecDecoder *dec = SWFDEC_ROOT_MOVIE (movie)->decoder; - loaded = dec->frames_loaded; - g_assert (loaded <= movie->n_frames); - } else { - loaded = movie->n_frames; - } - if (loaded < (guint) frame) - swfdec_script_skip_actions (cx, jump); - } else { - SWFDEC_ERROR ("no movie to WaitForFrame2 on"); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_wait_for_frame (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie = swfdec_action_get_target (cx); - guint frame, jump, loaded; - - if (len != 3) { - SWFDEC_ERROR ("WaitForFrame action length invalid (is %u, should be 3", len); - return JS_TRUE; - } - if (movie == NULL) { - SWFDEC_ERROR ("no movie for WaitForFrame"); - return JS_TRUE; - } - - frame = GUINT16_FROM_LE (*((guint16 *) data)); - jump = data[2]; - if (SWFDEC_IS_ROOT_MOVIE (movie)) { - SwfdecDecoder *dec = SWFDEC_ROOT_MOVIE (movie)->decoder; - loaded = dec->frames_loaded; - g_assert (loaded <= movie->n_frames); - } else { - loaded = movie->n_frames; - } - if (loaded < frame) - swfdec_script_skip_actions (cx, jump); - return JS_TRUE; -} - -static JSBool -swfdec_action_constant_pool (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecConstantPool *pool; - - pool = swfdec_constant_pool_new_from_action (data, len); - if (pool == NULL) - return JS_FALSE; - if (cx->fp->constant_pool) - swfdec_constant_pool_free (cx->fp->constant_pool); - cx->fp->constant_pool = pool; - return JS_TRUE; -} - -static JSBool -swfdec_action_push (JSContext *cx, guint action, const guint8 *data, guint len) -{ - /* FIXME: supply API for this */ - SwfdecBits bits; - guint stackspace = cx->fp->spend - cx->fp->sp; - - swfdec_bits_init_data (&bits, data, len); - while (swfdec_bits_left (&bits) && stackspace-- > 0) { - guint type = swfdec_bits_get_u8 (&bits); - SWFDEC_LOG ("push type %u", type); - switch (type) { - case 0: /* string */ - { - const char *s = swfdec_bits_skip_string (&bits); - if (!swfdec_action_push_string (cx, s)) - return JS_FALSE; - break; - } - case 1: /* float */ - { - double d = swfdec_bits_get_float (&bits); - if (!JS_NewDoubleValue (cx, d, cx->fp->sp)) - return JS_FALSE; - cx->fp->sp++; - break; - } - case 2: /* null */ - *cx->fp->sp++ = JSVAL_NULL; - break; - case 3: /* undefined */ - *cx->fp->sp++ = JSVAL_VOID; - break; - case 4: /* register */ - { - guint regnum = swfdec_bits_get_u8 (&bits); - if (!swfdec_action_has_register (cx, regnum)) { - SWFDEC_ERROR ("cannot Push register %u: not enough registers", regnum); - return JS_FALSE; - } - *cx->fp->sp++ = cx->fp->vars[regnum]; - break; - } - case 5: /* boolean */ - *cx->fp->sp++ = swfdec_bits_get_u8 (&bits) ? JSVAL_TRUE : JSVAL_FALSE; - break; - case 6: /* double */ - { - double d = swfdec_bits_get_double (&bits); - if (!JS_NewDoubleValue (cx, d, cx->fp->sp)) - return JS_FALSE; - cx->fp->sp++; - break; - } - case 7: /* 32bit int */ - { - int i = swfdec_bits_get_u32 (&bits); - *cx->fp->sp++ = INT_TO_JSVAL (i); - break; - } - case 8: /* 8bit ConstantPool address */ - { - guint i = swfdec_bits_get_u8 (&bits); - SwfdecConstantPool *pool = cx->fp->constant_pool; - if (pool == NULL) { - SWFDEC_ERROR ("no constant pool to push from"); - return JS_FALSE; - } - if (i >= swfdec_constant_pool_size (pool)) { - SWFDEC_ERROR ("constant pool index %u too high - only %u elements", - i, swfdec_constant_pool_size (pool)); - return JS_FALSE; - } - if (!swfdec_action_push_string (cx, swfdec_constant_pool_get (pool, i))) - return JS_FALSE; - break; - } - case 9: /* 16bit ConstantPool address */ - { - guint i = swfdec_bits_get_u16 (&bits); - SwfdecConstantPool *pool = cx->fp->constant_pool; - if (pool == NULL) { - SWFDEC_ERROR ("no constant pool to push from"); - return JS_FALSE; - } - if (i >= swfdec_constant_pool_size (pool)) { - SWFDEC_ERROR ("constant pool index %u too high - only %u elements", - i, swfdec_constant_pool_size (pool)); - return JS_FALSE; - } - if (!swfdec_action_push_string (cx, swfdec_constant_pool_get (pool, i))) - return JS_FALSE; - break; - } - default: - SWFDEC_ERROR ("Push: type %u not implemented", type); - return JS_FALSE; - } - } - return swfdec_bits_left (&bits) ? JS_FALSE : JS_TRUE; -} - -static JSBool -swfdec_action_get_variable (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *s; - - s = swfdec_js_to_string (cx, cx->fp->sp[-1]); - if (s == NULL) - return JS_FALSE; - cx->fp->sp[-1] = swfdec_js_eval (cx, NULL, s); -#ifdef SWFDEC_WARN_MISSING_PROPERTIES - if (cx->fp->sp[-1] == JSVAL_VOID) { - SWFDEC_WARNING ("no variable named %s", s); - } -#endif - return JS_TRUE; -} - -static JSBool -swfdec_action_set_variable (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *s; - - s = swfdec_js_to_string (cx, cx->fp->sp[-2]); - if (s == NULL) - return JS_FALSE; - - swfdec_js_eval_set (cx, NULL, s, cx->fp->sp[-1]); - cx->fp->sp -= 2; - return JS_TRUE; -} - -static JSBool -swfdec_action_trace (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecPlayer *player = JS_GetContextPrivate (cx); - const char *bytes; - - bytes = swfdec_js_to_string (cx, cx->fp->sp[-1]); - cx->fp->sp--; - if (bytes == NULL) - return JS_TRUE; - - swfdec_player_trace (player, bytes); - return JS_TRUE; -} - -/** - * swfdec_action_invoke: - * @cx: the #JSContext - * @n_args: number of arguments - * - * This function is similar to js_Invoke, however it uses a reversed stack - * order. sp[-1] has to be the function to call, sp[-2] will be the object the - * function is called on, sp[-3] is the first argument, followed by the rest of - * the arguments. The function removes all of these argumends from the stack - * and pushes the return value on top. - * - * Returns: JS_TRUE on success, JS_FALSE on failure. - **/ -static JSBool -swfdec_action_call (JSContext *cx, guint n_args, guint flags) -{ - JSStackFrame *fp = cx->fp; - int i, j; - jsval tmp; - guint stacksize; - - stacksize = fp->sp - fp->spbase; - g_assert (stacksize >= 2); - if (n_args + 2 > stacksize) { - SWFDEC_WARNING ("broken script. Want %u arguments, only got %u", n_args, stacksize - 2); - n_args = stacksize - 2; - if (!swfdec_script_ensure_stack (cx, n_args + 2)) - return JS_FALSE; - } - - j = -1; - i = - (n_args + 2); - while (i < j) { - tmp = fp->sp[j]; - fp->sp[j] = fp->sp[i]; - fp->sp[i] = tmp; - j--; - i++; - } - return js_Invoke (cx, n_args, flags); -} - -/* FIXME: lots of overlap with swfdec_action_call_method */ -static JSBool -swfdec_action_call_function (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSStackFrame *fp = cx->fp; - const char *s; - guint32 n_args; - JSObject *obj, *pobj; - JSProperty *prop; - jsval fun; - JSAtom *atom; - - if (!swfdec_script_ensure_stack (cx, 2)) - return JS_FALSE; - s = swfdec_js_to_string (cx, fp->sp[-1]); - if (s == NULL) - return JS_FALSE; - if (!JS_ValueToECMAUint32 (cx, fp->sp[-2], &n_args)) - return JS_FALSE; - - if (!(atom = js_Atomize (cx, s, strlen (s), 0)) || - !js_FindProperty (cx, (jsid) atom, &obj, &pobj, &prop)) - return JS_FALSE; - if (!JS_GetProperty (cx, obj, s, &fun)) - return JS_FALSE; - if (!JSVAL_IS_OBJECT (fun)) { - /* FIXME: figure out what class we operate on */ - SWFDEC_WARNING ("%s is not a function", s); - } - fp->sp[-1] = fun; - fp->sp[-2] = OBJECT_TO_JSVAL (obj); - swfdec_action_call (cx, n_args, 0); - return JS_TRUE; -} - -static JSBool -swfdec_action_call_method (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSStackFrame *fp = cx->fp; - const char *s; - guint32 n_args; - JSObject *obj; - jsval fun; - - if (!swfdec_script_ensure_stack (cx, 3)) - return JS_FALSE; - if (fp->sp[-1] == JSVAL_VOID) { - s = ""; - } else { - s = swfdec_js_to_string (cx, fp->sp[-1]); - if (s == NULL) - return JS_FALSE; - } - if (!JS_ValueToECMAUint32 (cx, fp->sp[-3], &n_args)) - return JS_FALSE; - - if (!JS_ValueToObject (cx, fp->sp[-2], &obj)) - return JS_FALSE; - if (obj == NULL) - goto fail; - if (s[0] == '\0') { - fun = OBJECT_TO_JSVAL (obj); - } else { - if (!JS_GetProperty (cx, obj, s, &fun)) - return JS_FALSE; - if (!JSVAL_IS_OBJECT (fun)) { - SWFDEC_WARNING ("%s:%s is not a function", JS_GetClass (obj)->name, s); - } - } - fp->sp--; - fp->sp[-1] = fun; - fp->sp[-2] = OBJECT_TO_JSVAL (obj); - swfdec_action_call (cx, n_args, 0); - return JS_TRUE; - -fail: - fp->sp -= 2 + n_args; - fp->sp[-1] = JSVAL_VOID; - return JS_TRUE; -} - -static JSBool -swfdec_action_pop (JSContext *cx, guint action, const guint8 *data, guint len) -{ - cx->fp->sp--; - return JS_TRUE; -} - -static const char * -swfdec_eval_jsval (JSContext *cx, JSObject *obj, jsval *val) -{ - if (JSVAL_IS_STRING (*val)) { - const char *bytes = swfdec_js_to_string (cx, *val); - if (bytes == NULL) - return NULL; - *val = swfdec_js_eval (cx, obj, bytes); - return bytes; - } else { - if (obj == NULL) { - obj = OBJ_THIS_OBJECT (cx, cx->fp->scopeChain); - } - *val = OBJECT_TO_JSVAL (obj); - return "."; - } -} - -static const char *properties[22] = { - "_x", "_y", "_xscale", "_yscale", "_currentframe", - "_totalframes", "_alpha", "_visible", "_width", "_height", - "_rotation", "_target", "_framesloaded", "_name", "_droptarget", - "_url", "_highquality", "_focusrect", "_soundbuftime", "_quality", - "_xmouse", "_ymouse" -}; - -static JSBool -swfdec_action_get_property (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval val; - SwfdecMovie *movie; - JSObject *jsobj; - guint32 id; - const char *bytes; - - if (!JS_ValueToECMAUint32 (cx, cx->fp->sp[-1], &id)) - return JS_FALSE; - val = cx->fp->sp[-2]; - bytes = swfdec_eval_jsval (cx, NULL, &val); - if (id > (((SwfdecScript *) cx->fp->swf)->version > 4 ? 21 : 18)) { - SWFDEC_WARNING ("trying to SetProperty %u, not allowed", id); - goto out; - } - - if (bytes == NULL) - return JS_FALSE; - if (*bytes == '\0') { - JSObject *pobj; - JSProperty *prop; - JSAtom *atom = js_Atomize (cx, properties[id], strlen (properties[id]), 0); - if (atom == NULL) - return JS_FALSE; - if (!js_FindProperty (cx, (jsid) atom, &jsobj, &pobj, &prop)) - return JS_FALSE; - if (!prop) - return JS_FALSE; - if (!OBJ_GET_PROPERTY (cx, jsobj, (jsid) prop->id, &val)) - return JS_FALSE; - } else { - movie = swfdec_scriptable_from_jsval (cx, val, SWFDEC_TYPE_MOVIE); - if (movie == NULL) { - SWFDEC_WARNING ("specified target does not reference a movie clip"); - goto out; - } - - jsobj = JSVAL_TO_OBJECT (val); - val = JSVAL_VOID; - - if (!JS_GetProperty (cx, jsobj, properties[id], &val)) - return JS_FALSE; - } - -out: - cx->fp->sp -= 1; - cx->fp->sp[-1] = val; - return JS_TRUE; -} - -static JSBool -swfdec_action_set_property (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval val; - SwfdecMovie *movie; - JSObject *jsobj; - guint32 id; - const char *bytes; - - val = cx->fp->sp[-3]; - if (!JS_ValueToECMAUint32 (cx, cx->fp->sp[-2], &id)) - return JS_FALSE; - bytes = swfdec_eval_jsval (cx, NULL, &val); - if (!bytes) - return JS_FALSE; - if (id > (((SwfdecScript *) cx->fp->swf)->version > 4 ? 21 : 18)) { - SWFDEC_WARNING ("trying to SetProperty %u, not allowed", id); - goto out; - } - if (*bytes == '\0' || *bytes == '.') - val = OBJECT_TO_JSVAL (cx->fp->varobj); - movie = swfdec_scriptable_from_jsval (cx, val, SWFDEC_TYPE_MOVIE); - if (movie == NULL) { - SWFDEC_WARNING ("specified target does not reference a movie clip"); - goto out; - } - jsobj = JSVAL_TO_OBJECT (val); - - if (!JS_SetProperty (cx, jsobj, properties[id], &cx->fp->sp[-1])) - return JS_FALSE; - -out: - cx->fp->sp -= 3; - return JS_TRUE; -} - -static JSBool -swfdec_action_binary (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval lval, rval; - double l, r; - - rval = cx->fp->sp[-1]; - lval = cx->fp->sp[-2]; - if (((SwfdecScript *) cx->fp->swf)->version < 7) { - l = swfdec_value_to_number (cx, lval); - r = swfdec_value_to_number (cx, rval); - } else { - if (!swfdec_value_to_number_7 (cx, lval, &l) || - !swfdec_value_to_number_7 (cx, rval, &r)) - return JS_FALSE; - } - cx->fp->sp--; - switch (action) { - case 0x0a: - l = l + r; - break; - case 0x0b: - l = l - r; - break; - case 0x0c: - l = l * r; - break; - case 0x0d: - if (((SwfdecScript *) cx->fp->swf)->version < 5) { - if (r == 0) { - JSString *str = JS_InternString (cx, "#ERROR#"); - if (str == NULL) - return JS_FALSE; - cx->fp->sp[-1] = STRING_TO_JSVAL (str); - return JS_TRUE; - } - } else if (((SwfdecScript *) cx->fp->swf)->version < 7) { - if (isnan (r)) - r = 0; - } - l = l / r; - break; - default: - g_assert_not_reached (); - return r; - } - return JS_NewNumberValue (cx, l, &cx->fp->sp[-1]); -} - -static JSString * -swfdec_action_to_string_5 (JSContext *cx, jsval val) -{ - if (JSVAL_IS_VOID (val)) - return cx->runtime->emptyString; - return js_ValueToString (cx, val); -} - -static JSBool -swfdec_action_add2_5 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval rval, lval; - gboolean cond; - - rval = cx->fp->sp[-1]; - lval = cx->fp->sp[-2]; - if ((cond = JSVAL_IS_STRING (lval)) || JSVAL_IS_STRING (rval)) { - JSString *str, *str2; - if (cond) { - str = JSVAL_TO_STRING (lval); - if ((str2 = swfdec_action_to_string_5 (cx, rval)) == NULL) - return JS_FALSE; - } else { - str2 = JSVAL_TO_STRING (rval); - if ((str = swfdec_action_to_string_5 (cx, lval)) == NULL) - return JS_FALSE; - } - str = js_ConcatStrings (cx, str, str2); - if (!str) - return JS_FALSE; - cx->fp->sp--; - cx->fp->sp[-1] = STRING_TO_JSVAL (str); - } else { - double d, d2; - d = swfdec_value_to_number (cx, lval); - d2 = swfdec_value_to_number (cx, rval); - d += d2; - cx->fp->sp--; - return JS_NewNumberValue(cx, d, &cx->fp->sp[-1]); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_add2_7 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval rval, lval; - gboolean cond; - - rval = cx->fp->sp[-1]; - lval = cx->fp->sp[-2]; - if ((cond = JSVAL_IS_STRING (lval)) || JSVAL_IS_STRING (rval)) { - JSString *str, *str2; - if (cond) { - str = JSVAL_TO_STRING (lval); - if ((str2 = js_ValueToString (cx, rval)) == NULL) - return JS_FALSE; - } else { - str2 = JSVAL_TO_STRING (rval); - if ((str = js_ValueToString (cx, lval)) == NULL) - return JS_FALSE; - } - str = js_ConcatStrings (cx, str, str2); - if (!str) - return JS_FALSE; - cx->fp->sp--; - cx->fp->sp[-1] = STRING_TO_JSVAL (str); - } else { - double d, d2; - if (!swfdec_value_to_number_7 (cx, lval, &d) || - !swfdec_value_to_number_7 (cx, rval, &d2)) - return JS_FALSE; - d += d2; - cx->fp->sp--; - return JS_NewNumberValue(cx, d, &cx->fp->sp[-1]); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_get_member (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *s; - jsval o; - - s = swfdec_js_to_string (cx, cx->fp->sp[-1]); - if (s == NULL) - return JS_FALSE; - - o = cx->fp->sp[-2]; - if (JSVAL_IS_OBJECT (o) && !JSVAL_IS_NULL (o)) { - if (!JS_GetProperty (cx, JSVAL_TO_OBJECT (o), s, &cx->fp->sp[-2])) - return JS_FALSE; -#ifdef SWFDEC_WARN_MISSING_PROPERTIES - if (cx->fp->sp[-2] == JSVAL_VOID) { - const JSClass *clasp = JS_GetClass (JSVAL_TO_OBJECT (o)); - if (clasp != &js_ObjectClass) { - SWFDEC_WARNING ("no variable named %s:%s", clasp->name, s); - } - } -#endif - } else { - cx->fp->sp[-2] = JSVAL_VOID; - } - cx->fp->sp--; - return JS_TRUE; -} - -static JSBool -swfdec_action_set_member (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *s; - - s = swfdec_js_to_string (cx, cx->fp->sp[-2]); - if (s == NULL) - return JS_FALSE; - - if (JSVAL_IS_OBJECT (cx->fp->sp[-3]) && !JSVAL_IS_NULL (cx->fp->sp[-3])) { - if (!JS_SetProperty (cx, JSVAL_TO_OBJECT (cx->fp->sp[-3]), s, &cx->fp->sp[-1])) - return JS_FALSE; - } - cx->fp->sp -= 3; - return JS_TRUE; -} - -static JSBool -swfdec_action_new_comparison_6 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval lval, rval; - double d, d2; - - rval = cx->fp->sp[-1]; - lval = cx->fp->sp[-2]; - cx->fp->sp--; - d = swfdec_value_to_number (cx, lval); - d2 = swfdec_value_to_number (cx, rval); - if (action == 0x48) - cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (d < d2); - else - cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (d > d2); - return JS_TRUE; -} - -static JSBool -swfdec_action_new_comparison_7 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval lval, rval; - - rval = cx->fp->sp[-1]; - lval = cx->fp->sp[-2]; - cx->fp->sp--; - if (JSVAL_IS_VOID (rval) || JSVAL_IS_VOID (lval)) { - cx->fp->sp[-1] = JSVAL_VOID; - } else if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { - int comp = JS_CompareStrings (JSVAL_TO_STRING (lval), JSVAL_TO_STRING (rval)); - cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (action == 0x48 ? comp < 0 : comp > 0); - } else { - double d, d2; - if (!JS_ValueToNumber(cx, lval, &d) || - !JS_ValueToNumber(cx, rval, &d2)) - return JS_FALSE; - cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (action == 0x48 ? d < d2 : d > d2); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_not_4 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double d; - - d = swfdec_value_to_number (cx, cx->fp->sp[-1]); - cx->fp->sp[-1] = INT_TO_JSVAL (d == 0 ? 1 : 0); - return JS_TRUE; -} - -static JSBool -swfdec_action_not_5 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double d; - - d = swfdec_value_to_number (cx, cx->fp->sp[-1]); - cx->fp->sp[-1] = d == 0 ? JSVAL_TRUE : JSVAL_FALSE; - return JS_TRUE; -} - -static JSBool -swfdec_action_jump (JSContext *cx, guint action, const guint8 *data, guint len) -{ - if (len != 2) { - SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len); - return JS_FALSE; - } - cx->fp->pc += 5 + GINT16_FROM_LE (*((gint16*) data)); - return JS_TRUE; -} - -static JSBool -swfdec_action_if (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double d; - - if (len != 2) { - SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len); - return JS_FALSE; - } - d = swfdec_value_to_number (cx, cx->fp->sp[-1]); - cx->fp->sp--; - if (d != 0) - cx->fp->pc += 5 + GINT16_FROM_LE (*((gint16*) data)); - return JS_TRUE; -} - -static JSBool -swfdec_action_decrement (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double d; - - d = swfdec_value_to_number (cx, cx->fp->sp[-1]); - d--; - return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); -} - -static JSBool -swfdec_action_increment (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double d; - - d = swfdec_value_to_number (cx, cx->fp->sp[-1]); - d++; - return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); -} - -static JSBool -swfdec_action_get_url (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie; - SwfdecBits bits; - const char *url, *target; - - swfdec_bits_init_data (&bits, data, len); - url = swfdec_bits_skip_string (&bits); - target = swfdec_bits_skip_string (&bits); - if (swfdec_bits_left (&bits)) { - SWFDEC_WARNING ("leftover bytes in GetURL action"); - } - movie = swfdec_action_get_target (cx); - if (movie) - swfdec_root_movie_load (SWFDEC_ROOT_MOVIE (movie->root), url, target); - else - SWFDEC_WARNING ("no movie to load"); - return JS_TRUE; -} - -static JSBool -swfdec_action_get_url2 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *target, *url; - guint method; - SwfdecMovie *movie; - - if (len != 1) { - SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len); - return JS_FALSE; - } - target = swfdec_js_to_string (cx, cx->fp->sp[-1]); - url = swfdec_js_to_string (cx, cx->fp->sp[-2]); - if (target == NULL || url == NULL) - return JS_FALSE; - method = data[0] >> 6; - if (method == 3) { - SWFDEC_ERROR ("GetURL method 3 invalid"); - method = 0; - } - if (method) { - SWFDEC_ERROR ("FIXME: implement encoding variables using %s", method == 1 ? "GET" : "POST"); - } - if (data[0] & 2) { - SWFDEC_ERROR ("FIXME: implement LoadTarget"); - } - if (data[0] & 1) { - SWFDEC_ERROR ("FIXME: implement LoadVariables"); - } - movie = swfdec_action_get_target (cx); - if (movie) - swfdec_root_movie_load (SWFDEC_ROOT_MOVIE (movie->root), url, target); - else - SWFDEC_WARNING ("no movie to load"); - cx->fp->sp -= 2; - return JS_TRUE; -} - -static JSBool -swfdec_action_string_add (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSString *lval, *rval; - - rval = JS_ValueToString (cx, cx->fp->sp[-1]); - lval = JS_ValueToString (cx, cx->fp->sp[-2]); - if (lval == NULL || rval == NULL) - return FALSE; - lval = JS_ConcatStrings (cx, lval, rval); - if (lval == NULL) - return FALSE; - cx->fp->sp--; - cx->fp->sp[-1] = STRING_TO_JSVAL (lval); - return JS_TRUE; -} - -static JSBool -swfdec_action_push_duplicate (JSContext *cx, guint action, const guint8 *data, guint len) -{ - cx->fp->sp++; - cx->fp->sp[-1] = cx->fp->sp[-2]; - return JS_TRUE; -} - -static JSBool -swfdec_action_random_number (JSContext *cx, guint action, const guint8 *data, guint len) -{ - gint32 max, result; - - if (!JS_ValueToECMAInt32 (cx, cx->fp->sp[-1], &max)) - return JS_FALSE; - - if (max <= 0) - result = 0; - else - result = g_random_int_range (0, max); - - return JS_NewNumberValue(cx, result, &cx->fp->sp[-1]); -} - -static JSBool -swfdec_action_old_compare (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval rval, lval; - double l, r; - JSBool cond; - - rval = cx->fp->sp[-1]; - lval = cx->fp->sp[-2]; - l = swfdec_value_to_number (cx, lval); - r = swfdec_value_to_number (cx, rval); - switch (action) { - case 0x0e: - cond = l == r; - break; - case 0x0f: - cond = l < r; - break; - default: - g_assert_not_reached (); - return JS_FALSE; - } - cx->fp->sp--; - if (((SwfdecScript *) cx->fp->swf)->version < 5) { - cx->fp->sp[-1] = INT_TO_JSVAL (cond ? 1 : 0); - } else { - cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (cond); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_equals2 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval rval, lval; - int ltag, rtag; - JSBool cond; - - rval = cx->fp->sp[-1]; - lval = cx->fp->sp[-2]; - ltag = JSVAL_TAG(lval); - rtag = JSVAL_TAG(rval); - if (ltag == rtag) { - if (ltag == JSVAL_STRING) { - cond = js_CompareStrings (JSVAL_TO_STRING (lval), JSVAL_TO_STRING (rval)) == 0; - } else if (ltag == JSVAL_DOUBLE) { - cond = *JSVAL_TO_DOUBLE(lval) == *JSVAL_TO_DOUBLE(rval); - } else { - cond = lval == rval; - } - } else { - if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { - cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)); - } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { - cond = JS_FALSE; - } else { - if (ltag == JSVAL_OBJECT) { - if (!OBJ_DEFAULT_VALUE (cx, JSVAL_TO_OBJECT(lval), 0, &lval)) - return JS_FALSE; - ltag = JSVAL_TAG(lval); - } else if (rtag == JSVAL_OBJECT) { - if (!OBJ_DEFAULT_VALUE (cx, JSVAL_TO_OBJECT(rval), 0, &rval)) - return JS_FALSE; - rtag = JSVAL_TAG(rval); - } - if (ltag == JSVAL_STRING && rtag == JSVAL_STRING) { - cond = js_CompareStrings (JSVAL_TO_STRING (lval), JSVAL_TO_STRING (rval)) == 0; - } else { - double d, d2; - if (!JS_ValueToNumber (cx, lval, &d) || - !JS_ValueToNumber (cx, rval, &d2)) - return JS_FALSE; - cond = d == d2; - } - } - } - cx->fp->sp--; - cx->fp->sp[-1] = BOOLEAN_TO_JSVAL (cond); - return JS_TRUE; -} - -static JSBool -swfdec_action_do_set_target (JSContext *cx, JSObject *target) -{ - JSObject *with; - - /* FIXME: this whole function stops working the moment it's used together - * with With */ - with = js_NewObject(cx, &js_WithClass, target, cx->fp->scopeChain); - if (!with) - return JS_FALSE; - cx->fp->scopeChain = with; - return JS_TRUE; -} - -static JSBool -swfdec_action_do_unset_target (JSContext *cx) -{ - if (JS_GetClass (cx->fp->scopeChain) != &js_WithClass) { - SWFDEC_ERROR ("Cannot unset target: scope chain contains no with object"); - return JS_TRUE; - } - cx->fp->scopeChain = JS_GetParent (cx, cx->fp->scopeChain); - return JS_TRUE; -} - -static JSBool -swfdec_action_set_target (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval target; - - if (!memchr (data, 0, len)) { - SWFDEC_ERROR ("SetTarget action does not specify a string"); - return JS_FALSE; - } - if (*data == '\0') - return swfdec_action_do_unset_target (cx); - target = swfdec_js_eval (cx, NULL, (const char *) data); - if (!JSVAL_IS_OBJECT (target) || JSVAL_IS_NULL (target)) { - SWFDEC_WARNING ("target is not an object"); - return JS_TRUE; - } - return swfdec_action_do_set_target (cx, JSVAL_TO_OBJECT (target)); -} - -static JSBool -swfdec_action_set_target2 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval val; - - val = cx->fp->sp[-1]; - cx->fp->sp--; - if (!JSVAL_IS_OBJECT (val) || JSVAL_IS_NULL (val)) { - SWFDEC_WARNING ("target is not an object"); - return JS_TRUE; - } - return swfdec_action_do_set_target (cx, JSVAL_TO_OBJECT (val)); -} - -static JSBool -swfdec_action_start_drag (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSStackFrame *fp = cx->fp; - guint n_args = 1; - - if (!swfdec_script_ensure_stack (cx, 3)) - return JS_FALSE; - if (!swfdec_eval_jsval (cx, NULL, &fp->sp[-1])) - return JS_FALSE; - if (swfdec_value_to_number (cx, fp->sp[-3])) { - jsval tmp; - if (!swfdec_script_ensure_stack (cx, 7)) - return JS_FALSE; - n_args = 5; - /* yay for order */ - tmp = fp->sp[-4]; - fp->sp[-4] = fp->sp[-7]; - fp->sp[-7] = tmp; - tmp = fp->sp[-6]; - fp->sp[-6] = fp->sp[-5]; - fp->sp[-5] = tmp; - } - if (!JSVAL_IS_OBJECT (fp->sp[-1]) || JSVAL_IS_NULL (fp->sp[-1])) { - fp->sp -= n_args + 2; - return JS_TRUE; - } - fp->sp[-3] = fp->sp[-2]; - fp->sp[-2] = fp->sp[-1]; - if (!JS_GetProperty (cx, JSVAL_TO_OBJECT (fp->sp[-2]), "startDrag", &fp->sp[-1])) - return JS_FALSE; - swfdec_action_call (cx, n_args, 0); - fp->sp--; - return JS_TRUE; -} - -static JSBool -swfdec_action_end_drag (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecPlayer *player = JS_GetContextPrivate (cx); - swfdec_player_set_drag_movie (player, NULL, FALSE, NULL); - return JS_TRUE; -} - -static JSBool -swfdec_action_stop_sounds (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecPlayer *player = JS_GetContextPrivate (cx); - - swfdec_player_stop_all_sounds (player); - return JS_TRUE; -} - -static JSBool -swfdec_action_new_object (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSStackFrame *fp = cx->fp; - jsval constructor; - JSObject *object; - guint n_args; - const char *name; - - if (!swfdec_script_ensure_stack (cx, 2)) - return JS_FALSE; - constructor = fp->sp[-1]; - name = swfdec_eval_jsval (cx, NULL, &constructor); - if (name == NULL) - return JS_FALSE; - if (!JS_ValueToECMAUint32 (cx, fp->sp[-2], &n_args)) - return JS_FALSE; - if (constructor == JSVAL_VOID) { - SWFDEC_WARNING ("no constructor for %s", name); - } - fp->sp[-1] = constructor; - - if (!swfdec_js_construct_object (cx, NULL, constructor, &object)) - return JS_FALSE; - if (object == NULL) - goto fail; - fp->sp[-2] = OBJECT_TO_JSVAL (object); - if (!swfdec_action_call (cx, n_args, JSINVOKE_CONSTRUCT)) - return JS_FALSE; - fp->sp[-1] = OBJECT_TO_JSVAL (object); - return JS_TRUE; - -fail: - fp->sp -= n_args + 1; - fp->sp[-1] = JSVAL_VOID; - return JS_TRUE; -} - -static JSBool -swfdec_action_new_method (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSStackFrame *fp = cx->fp; - const char *s; - guint32 n_args; - JSObject *object; - jsval constructor; - - if (!swfdec_script_ensure_stack (cx, 3)) - return JS_FALSE; - s = swfdec_js_to_string (cx, fp->sp[-1]); - if (s == NULL) - return JS_FALSE; - if (!JS_ValueToECMAUint32 (cx, fp->sp[-3], &n_args)) - return JS_FALSE; - - if (!JS_ValueToObject (cx, fp->sp[-2], &object)) - return JS_FALSE; - if (object == NULL) - goto fail; - if (s[0] == '\0') { - constructor = OBJECT_TO_JSVAL (object); - } else { - if (!JS_GetProperty (cx, object, s, &constructor)) - return JS_FALSE; - if (!JSVAL_IS_OBJECT (constructor)) { - SWFDEC_WARNING ("%s:%s is not a function", JS_GetClass (object)->name, s); - } - } - fp->sp[-1] = OBJECT_TO_JSVAL (constructor); - if (!swfdec_js_construct_object (cx, NULL, constructor, &object)) - return JS_FALSE; - if (object == NULL) - goto fail; - fp->sp[-2] = OBJECT_TO_JSVAL (object); - if (!swfdec_action_call (cx, n_args, JSINVOKE_CONSTRUCT)) - return JS_FALSE; - fp->sp[-1] = OBJECT_TO_JSVAL (object); - return JS_TRUE; - -fail: - fp->sp -= 2 + n_args; - fp->sp[-1] = JSVAL_VOID; - return JS_TRUE; -} - -static JSBool -swfdec_action_init_object (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSStackFrame *fp = cx->fp; - JSObject *object; - guint n_args; - gulong i; - - if (!JS_ValueToECMAUint32 (cx, fp->sp[-1], &n_args)) - return JS_FALSE; - if (!swfdec_script_ensure_stack (cx, 2 * n_args + 1)) - return JS_FALSE; - - object = JS_NewObject (cx, &js_ObjectClass, NULL, NULL); - if (object == NULL) - return JS_FALSE; - for (i = 0; i < n_args; i++) { - const char *s = swfdec_js_to_string (cx, fp->sp[-3 - 2 * i]); - if (s == NULL) - return JS_FALSE; - if (!JS_SetProperty (cx, object, s, &fp->sp[-2 - 2 * i])) - return JS_FALSE; - } - fp->sp -= 2 * n_args; - fp->sp[-1] = OBJECT_TO_JSVAL (object); - return JS_TRUE; -} - -static JSBool -swfdec_action_init_array (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSStackFrame *fp = cx->fp; - JSObject *array; - int i, j; - guint n_items; - - if (!JS_ValueToECMAUint32 (cx, fp->sp[-1], &n_items)) - return JS_FALSE; - if (!swfdec_script_ensure_stack (cx, n_items + 1)) - return JS_FALSE; - - /* items are the wrong order on the stack */ - j = - 1 - n_items; - for (i = - 2; i > j; i--, j++) { - jsval tmp = fp->sp[i]; - fp->sp[i] = fp->sp[j]; - fp->sp[j] = tmp; - } - array = JS_NewArrayObject (cx, n_items, fp->sp - n_items - 1); - if (array == NULL) - return JS_FALSE; - fp->sp -= n_items; - fp->sp[-1] = OBJECT_TO_JSVAL (array); - return JS_TRUE; -} - -static JSBool -swfdec_action_define_function (JSContext *cx, guint action, - const guint8 *data, guint len) -{ - const char *function_name; - guint i, n_args, size; - SwfdecBits bits; - JSFunction *fun; - SwfdecScript *script; - JSObject *scope; - gboolean has_preloads = FALSE; - guint flags = 0; - guint8 *preloads = NULL; - gboolean v2 = (action == 0x8e); - - swfdec_bits_init_data (&bits, data, len); - function_name = swfdec_bits_skip_string (&bits); - if (function_name == NULL) { - SWFDEC_ERROR ("could not parse function name"); - return JS_FALSE; - } - n_args = swfdec_bits_get_u16 (&bits); - scope = cx->fp->scopeChain; - script = cx->fp->swf; - if (script->version == 5) { - /* In Flash 5 there's only the root scope as a parent scope */ - JSObject *parent; - /* FIXME: this implementation is hacky (but it works) */ - while (JS_GetClass (scope) == &js_CallClass && (parent = JS_GetParent (cx, scope))) - scope = parent; - } - if (*function_name == '\0') { - /* anonymous function */ - fun = JS_NewFunction (cx, NULL, n_args, JSFUN_LAMBDA | JSFUN_HEAVYWEIGHT, - scope, NULL); - } else { - /* named function */ - fun = JS_NewFunction (cx, NULL, n_args, JSFUN_HEAVYWEIGHT, - scope, function_name); - } - if (fun == NULL) - return JS_FALSE; - if (v2) { - script->n_registers = swfdec_bits_get_u8 (&bits) + 1; - flags = swfdec_bits_get_u16 (&bits); - preloads = g_new0 (guint8, n_args); - } else { - script->n_registers = 5; - } - fun->nvars = script->n_registers; - for (i = 0; i < n_args; i++) { - JSAtom *atom; - const char *arg_name; - if (v2) { - guint preload = swfdec_bits_get_u8 (&bits); - if (preload && preload >= script->n_registers) { - SWFDEC_ERROR ("argument %u is preloaded into register %u out of %u", - i, preload, script->n_registers); - return JS_FALSE; - } - if (preload != 0) { - preloads[i] = preload; - swfdec_bits_skip_string (&bits); - has_preloads = TRUE; - continue; - } - } - arg_name = swfdec_bits_skip_string (&bits); - if (arg_name == NULL || *arg_name == '\0') { - SWFDEC_ERROR ("empty argument name not allowed"); - return JS_FALSE; - } - /* FIXME: check duplicate arguments */ - atom = js_Atomize (cx, arg_name, strlen (arg_name), 0); - if (atom == NULL) - return JS_FALSE; - if (!js_AddNativeProperty (cx, fun->object, (jsid) atom, - js_GetArgument, js_SetArgument, SPROP_INVALID_SLOT, - JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED, - SPROP_HAS_SHORTID, i)) { - return JS_FALSE; - } - } - if (preloads && !has_preloads) { - g_free (preloads); - preloads = NULL; - } - size = swfdec_bits_get_u16 (&bits); - /* check the script can be created */ - if (script->buffer->data + script->buffer->length < cx->fp->pc + 3 + len + size) { - SWFDEC_ERROR ("size of function is too big"); - return FALSE; - } else { - /* create the script */ - const char *name = NULL; - SwfdecBuffer *buffer = swfdec_buffer_new_subbuffer (script->buffer, - cx->fp->pc + 3 + len - script->buffer->data, size); - swfdec_bits_init (&bits, buffer); - if (*function_name) { - name = function_name; - } else if (cx->fp->sp > cx->fp->spbase) { - /* This is kind of a hack that uses a feature of the Adobe compiler: - * foo = function () {} is compiled as these actions: - * Push "foo", DefineFunction, SetVariable/SetMember - * With this knowledge we can inspect the topmost stack member, since - * it will contain the name this function will soon be assigned to. - */ - if (JSVAL_IS_STRING (cx->fp->sp[-1])) - name = JS_GetStringBytes (JSVAL_TO_STRING (cx->fp->sp[-1])); - } - if (name == NULL) - name = "unnamed_function"; - script = swfdec_script_new (&bits, name, ((SwfdecScript *) cx->fp->swf)->version); - swfdec_buffer_unref (buffer); - } - if (script == NULL) { - SWFDEC_ERROR ("failed to create script"); - g_free (preloads); - return JS_FALSE; - } - if (cx->fp->constant_pool) { - script->constant_pool = swfdec_constant_pool_get_area (cx->fp->swf, - cx->fp->constant_pool); - } - script->flags = flags; - script->preloads = preloads; - fun->swf = script; - swfdec_script_add_to_player (script, JS_GetContextPrivate (cx)); - /* attach the function */ - if (*function_name == '\0') { - if (cx->fp->sp >= cx->fp->spend) { - SWFDEC_ERROR ("not enough stack space available"); - return JS_FALSE; - } - *cx->fp->sp++ = OBJECT_TO_JSVAL (fun->object); - } else { - jsval val = OBJECT_TO_JSVAL (fun->object); - if (!JS_SetProperty (cx, cx->fp->varobj, function_name, &val)) - return JS_FALSE; - } - - /* update current context */ - cx->fp->pc += 3 + len + size; - return JS_TRUE; -} - -static JSBool -swfdec_action_bitwise (JSContext *cx, guint action, const guint8 *data, guint len) -{ - guint32 a, b; - double d; - - if (!JS_ValueToECMAUint32 (cx, cx->fp->sp[-1], &a) || - !JS_ValueToECMAUint32 (cx, cx->fp->sp[-2], &b)) - return JS_FALSE; - - switch (action) { - case 0x60: - d = (int) (a & b); - break; - case 0x61: - d = (int) (a | b); - break; - case 0x62: - d = (int) (a ^ b); - break; - default: - g_assert_not_reached (); - return JS_FALSE; - } - - cx->fp->sp--; - return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); -} - -static JSBool -swfdec_action_shift (JSContext *cx, guint action, const guint8 *data, guint len) -{ - guint32 amount, value; - double d; - - if (!JS_ValueToECMAUint32 (cx, cx->fp->sp[-1], &amount) || - !JS_ValueToECMAUint32 (cx, cx->fp->sp[-2], &value)) - return JS_FALSE; - - amount &= 31; - switch (action) { - case 0x63: - d = value << amount; - break; - case 0x64: - d = ((gint) value) >> amount; - break; - case 0x65: - d = ((guint) value) >> amount; - break; - default: - g_assert_not_reached (); - return JS_FALSE; - } - - cx->fp->sp--; - return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); -} - -static JSBool -swfdec_action_to_integer (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double d = swfdec_value_to_number (cx, cx->fp->sp[-1]); - - return JS_NewNumberValue (cx, (int) d, &cx->fp->sp[-1]); -} - -static JSBool -swfdec_action_target_path (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecMovie *movie = swfdec_scriptable_from_jsval (cx, cx->fp->sp[-1], SWFDEC_TYPE_MOVIE); - - if (movie == NULL) { - cx->fp->sp[-1] = JSVAL_VOID; - } else { - char *s = swfdec_movie_get_path (movie); - JSString *string = JS_NewStringCopyZ (cx, s); - g_free (s); - if (string == NULL) - return JS_FALSE; - cx->fp->sp[-1] = STRING_TO_JSVAL (string); - } - return JS_TRUE; -} - -static JSBool -swfdec_action_define_local (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *name; - - g_assert (cx->fp->scopeChain != NULL); - name = swfdec_js_to_string (cx, cx->fp->sp[-2]); - if (name == NULL) - return JS_FALSE; - if (!JS_SetProperty (cx, cx->fp->scopeChain, name, &cx->fp->sp[-1])) - return JS_FALSE; - cx->fp->sp -= 2; - return JS_TRUE; -} - -static JSBool -swfdec_action_define_local2 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *name; - jsval val = JSVAL_VOID; - - g_assert (cx->fp->scopeChain != NULL); - name = swfdec_js_to_string (cx, cx->fp->sp[-1]); - if (name == NULL) - return JS_FALSE; - if (!JS_SetProperty (cx, cx->fp->scopeChain, name, &val)) - return JS_FALSE; - cx->fp->sp--; - return JS_TRUE; -} - -static JSBool -swfdec_action_return (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecScript *script = cx->fp->swf; - - cx->fp->rval = cx->fp->sp[-1]; - cx->fp->pc = script->buffer->data + script->buffer->length; - cx->fp->sp--; - return JS_TRUE; -} - -static JSBool -swfdec_action_delete (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *name; - - cx->fp->sp -= 2; - name = swfdec_js_to_string (cx, cx->fp->sp[1]); - if (name == NULL) - return JS_FALSE; - if (!JSVAL_IS_OBJECT (cx->fp->sp[0])) - return JS_TRUE; - return JS_DeleteProperty (cx, JSVAL_TO_OBJECT (cx->fp->sp[0]), name); -} - -static JSBool -swfdec_action_delete2 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - const char *name; - JSObject *obj, *pobj; - JSProperty *prop; - JSAtom *atom; - - cx->fp->sp -= 1; - name = swfdec_js_to_string (cx, cx->fp->sp[1]); - if (name == NULL) - return JS_FALSE; - if (!(atom = js_Atomize (cx, name, strlen (name), 0)) || - !js_FindProperty (cx, (jsid) atom, &obj, &pobj, &prop)) - return JS_FALSE; - if (!pobj) - return JS_TRUE; - return JS_DeleteProperty (cx, pobj, name); -} - -static JSBool -swfdec_action_store_register (JSContext *cx, guint action, const guint8 *data, guint len) -{ - if (len != 1) { - SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len); - return JS_FALSE; - } - if (!swfdec_action_has_register (cx, *data)) { - SWFDEC_ERROR ("Cannot store into register %u, not enough registers", (guint) *data); - return JS_FALSE; - } - cx->fp->vars[*data] = cx->fp->sp[-1]; - return JS_TRUE; -} - -static JSBool -swfdec_action_modulo_5 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double x, y; - - x = swfdec_value_to_number (cx, cx->fp->sp[-1]); - y = swfdec_value_to_number (cx, cx->fp->sp[-2]); - cx->fp->sp--; - errno = 0; - x = fmod (x, y); - if (errno != 0) { - cx->fp->sp[-1] = DOUBLE_TO_JSVAL (cx->runtime->jsNaN); - return JS_TRUE; - } else { - return JS_NewNumberValue (cx, x, &cx->fp->sp[-1]); - } -} - -static JSBool -swfdec_action_modulo_7 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double x, y; - - if (!swfdec_value_to_number_7 (cx, cx->fp->sp[-1], &x) || - !swfdec_value_to_number_7 (cx, cx->fp->sp[-2], &y)) - return JS_FALSE; - cx->fp->sp--; - errno = 0; - x = fmod (x, y); - if (errno != 0) { - cx->fp->sp[-1] = DOUBLE_TO_JSVAL (cx->runtime->jsNaN); - return JS_TRUE; - } else { - return JS_NewNumberValue (cx, x, &cx->fp->sp[-1]); - } -} - -static JSBool -swfdec_action_swap (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval tmp = cx->fp->sp[-2]; - cx->fp->sp[-2] = cx->fp->sp[-1]; - cx->fp->sp[-1] = tmp; - return JS_TRUE; -} - -static JSBool -swfdec_action_to_number (JSContext *cx, guint action, const guint8 *data, guint len) -{ - double d; - if (!JS_ValueToNumber (cx, cx->fp->sp[-1], &d)) - return JS_FALSE; - return JS_NewNumberValue (cx, d, &cx->fp->sp[-1]); -} - -static JSBool -swfdec_action_to_string (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSString *s; - s = JS_ValueToString(cx, cx->fp->sp[-1]); - if (!s) - return JS_FALSE; - cx->fp->sp[-1] = STRING_TO_JSVAL (s); - return JS_TRUE; -} - -static JSBool -swfdec_action_type_of (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval val; - const char *type; - JSString *string; - - val = cx->fp->sp[-1]; - if (JSVAL_IS_NUMBER (val)) { - type = "number"; - } else if (JSVAL_IS_BOOLEAN (val)) { - type = "boolean"; - } else if (JSVAL_IS_STRING (val)) { - type = "string"; - } else if (JSVAL_IS_VOID (val)) { - type = "undefined"; - } else if (JSVAL_IS_NULL (val)) { - type = "null"; - } else if (JSVAL_IS_OBJECT (val)) { - JSObject *obj = JSVAL_TO_OBJECT (val); - if (swfdec_js_is_movieclip (cx, obj)) { - type = "movieclip"; - } else if (JS_ObjectIsFunction (cx, obj)) { - type = "function"; - } else { - type = "object"; - } - } else { - g_assert_not_reached (); - return JS_FALSE; - } - /* can't use InternString here because of case sensitivity issues */ - string = JS_NewStringCopyZ (cx, type); - if (string == NULL) - return JS_FALSE; - cx->fp->sp[-1] = STRING_TO_JSVAL (string); - return JS_TRUE; -} - -static JSBool -swfdec_action_get_time (JSContext *cx, guint action, const guint8 *data, guint len) -{ - SwfdecPlayer *player = JS_GetContextPrivate (cx); - - *cx->fp->sp++ = INT_TO_JSVAL ((int) SWFDEC_TICKS_TO_MSECS (player->time)); - return JS_TRUE; -} - -static JSBool -swfdec_action_extends (JSContext *cx, guint action, const guint8 *data, guint len) -{ - jsval superclass, subclass, proto; - JSObject *prototype; - - superclass = cx->fp->sp[-1]; - subclass = cx->fp->sp[-2]; - cx->fp->sp -= 2; - if (!JSVAL_IS_OBJECT (superclass) || superclass == JSVAL_NULL || - !JSVAL_IS_OBJECT (subclass) || subclass == JSVAL_NULL) { - SWFDEC_ERROR ("superclass or subclass aren't objects"); - return JS_TRUE; - } - if (!JS_GetProperty (cx, JSVAL_TO_OBJECT (superclass), "prototype", &proto) || - !JSVAL_IS_OBJECT (proto)) - return JS_FALSE; - prototype = JS_NewObject (cx, NULL, JSVAL_TO_OBJECT (proto), NULL); - if (prototype == NULL) - return JS_FALSE; - proto = OBJECT_TO_JSVAL (prototype); - if (!JS_SetProperty (cx, prototype, "__constructor__", &superclass) || - !JS_SetProperty (cx, JSVAL_TO_OBJECT (subclass), "prototype", &proto)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -swfdec_action_enumerate2 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSObject *obj; - JSIdArray *array; - guint i; - - if (!JSVAL_IS_OBJECT (cx->fp->sp[-1]) || cx->fp->sp[-1] == JSVAL_NULL) { - SWFDEC_ERROR ("Enumerate2 called without an object"); - cx->fp->sp[-1] = JSVAL_NULL; - return JS_TRUE; - } - obj = JSVAL_TO_OBJECT (cx->fp->sp[-1]); - cx->fp->sp[-1] = JSVAL_NULL; - array = JS_Enumerate (cx, obj); - if (!array) - return JS_FALSE; - if ((guint) (cx->fp->spend - cx->fp->sp) < array->length) { - SWFDEC_ERROR ("FIXME: not enough stack space, need %u, got %td", - array->length, cx->fp->spend - cx->fp->sp); - JS_DestroyIdArray (cx, array); - return JS_FALSE; - } - for (i = 0; i < array->length; i++) { - if (!JS_IdToValue (cx, array->vector[i], cx->fp->sp++)) { - JS_DestroyIdArray (cx, array); - return JS_FALSE; - } - } - JS_DestroyIdArray (cx, array); - return JS_TRUE; -} - -static JSBool -swfdec_action_logical_5 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSBool l, r; - - l = swfdec_value_to_boolean_5 (cx, cx->fp->sp[-1]); - r = swfdec_value_to_boolean_5 (cx, cx->fp->sp[-2]); - - cx->fp->sp--; - if (action == 0x10) - cx->fp->sp[-1] = l && r ? JSVAL_TRUE : JSVAL_FALSE; - else - cx->fp->sp[-1] = l || r ? JSVAL_TRUE : JSVAL_FALSE; - return JS_TRUE; -} - -static JSBool -swfdec_action_logical_7 (JSContext *cx, guint action, const guint8 *data, guint len) -{ - JSBool l, r; - - l = swfdec_value_to_boolean_7 (cx, cx->fp->sp[-1]); - r = swfdec_value_to_boolean_7 (cx, cx->fp->sp[-2]); - - cx->fp->sp--; - if (action == 0x10) - cx->fp->sp[-1] = l && r ? JSVAL_TRUE : JSVAL_FALSE; - else - cx->fp->sp[-1] = l || r ? JSVAL_TRUE : JSVAL_FALSE; - return JS_TRUE; -} - -/*** PRINT FUNCTIONS ***/ - -static char * -swfdec_action_print_store_register (guint action, const guint8 *data, guint len) -{ - if (len != 1) { - SWFDEC_ERROR ("StoreRegister action requires a length of 1, but got %u", len); - return NULL; - } - return g_strdup_printf ("StoreRegister %u", (guint) *data); -} - -static char * -swfdec_action_print_set_target (guint action, const guint8 *data, guint len) -{ - if (!memchr (data, 0, len)) { - SWFDEC_ERROR ("SetTarget action does not specify a string"); - return JS_FALSE; - } - return g_strconcat ("SetTarget ", data, NULL); -} - -static char * -swfdec_action_print_define_function (guint action, const guint8 *data, guint len) -{ - SwfdecBits bits; - GString *string; - const char *function_name; - guint i, n_args, size; - gboolean v2 = (action == 0x8e); - - string = g_string_new (v2 ? "DefineFunction2 " : "DefineFunction "); - swfdec_bits_init_data (&bits, data, len); - function_name = swfdec_bits_get_string (&bits); - if (function_name == NULL) { - SWFDEC_ERROR ("could not parse function name"); - g_string_free (string, TRUE); - return NULL; - } - if (*function_name) { - g_string_append (string, function_name); - g_string_append_c (string, ' '); - } - n_args = swfdec_bits_get_u16 (&bits); - g_string_append_c (string, '('); - if (v2) { - /* n_regs = */ swfdec_bits_get_u8 (&bits); - /* flags = */ swfdec_bits_get_u16 (&bits); - } - - for (i = 0; i < n_args; i++) { - guint preload; - const char *arg_name; - if (v2) - preload = swfdec_bits_get_u8 (&bits); - else - preload = 0; - arg_name = swfdec_bits_get_string (&bits); - if (preload == 0 && (arg_name == NULL || *arg_name == '\0')) { - SWFDEC_ERROR ("empty argument name not allowed"); - g_string_free (string, TRUE); - return NULL; - } - if (i) - g_string_append (string, ", "); - if (preload) - g_string_append_printf (string, "PRELOAD %u", preload); - else - g_string_append (string, arg_name); - } - g_string_append_c (string, ')'); - size = swfdec_bits_get_u16 (&bits); - g_string_append_printf (string, " %u", size); - return g_string_free (string, FALSE); -} - -static char * -swfdec_action_print_get_url2 (guint action, const guint8 *data, guint len) -{ - guint method; - - if (len != 1) { - SWFDEC_ERROR ("GetURL2 requires 1 byte of data, not %u", len); - return NULL; - } - method = data[0] >> 6; - if (method == 3) { - SWFDEC_ERROR ("GetURL method 3 invalid"); - method = 0; - } - if (method) { - SWFDEC_ERROR ("FIXME: implement encoding variables using %s", method == 1 ? "GET" : "POST"); - } - return g_strdup_printf ("GetURL2%s%s%s", method == 0 ? "" : (method == 1 ? " GET" : " POST"), - data[0] & 2 ? " LoadTarget" : "", data[0] & 1 ? " LoadVariables" : ""); -} - -static char * -swfdec_action_print_get_url (guint action, const guint8 *data, guint len) -{ - SwfdecBits bits; - const char *url, *target; - - swfdec_bits_init_data (&bits, data, len); - url = swfdec_bits_skip_string (&bits); - target = swfdec_bits_skip_string (&bits); - if (swfdec_bits_left (&bits)) { - SWFDEC_WARNING ("leftover bytes in GetURL action"); - } - return g_strdup_printf ("GetURL %s %s", url, target); -} - -static char * -swfdec_action_print_if (guint action, const guint8 *data, guint len) -{ - if (len != 2) { - SWFDEC_ERROR ("If action length invalid (is %u, should be 2", len); - return NULL; - } - return g_strdup_printf ("If %d", GINT16_FROM_LE (*((gint16*) data))); -} - -static char * -swfdec_action_print_jump (guint action, const guint8 *data, guint len) -{ - if (len != 2) { - SWFDEC_ERROR ("Jump action length invalid (is %u, should be 2", len); - return NULL; - } - return g_strdup_printf ("Jump %d", GINT16_FROM_LE (*((gint16*) data))); -} - -static char * -swfdec_action_print_push (guint action, const guint8 *data, guint len) -{ - gboolean first = TRUE; - SwfdecBits bits; - GString *string = g_string_new ("Push"); - - swfdec_bits_init_data (&bits, data, len); - while (swfdec_bits_left (&bits)) { - guint type = swfdec_bits_get_u8 (&bits); - if (first) - g_string_append (string, " "); - else - g_string_append (string, ", "); - first = FALSE; - switch (type) { - case 0: /* string */ - { - const char *s = swfdec_bits_skip_string (&bits); - if (!s) { - g_string_free (string, TRUE); - return NULL; - } - g_string_append_c (string, '"'); - g_string_append (string, s); - g_string_append_c (string, '"'); - break; - } - case 1: /* float */ - g_string_append_printf (string, "%g", swfdec_bits_get_float (&bits)); - break; - case 2: /* null */ - g_string_append (string, "null"); - break; - case 3: /* undefined */ - g_string_append (string, "void"); - break; - case 4: /* register */ - g_string_append_printf (string, "Register %u", swfdec_bits_get_u8 (&bits)); - break; - case 5: /* boolean */ - g_string_append (string, swfdec_bits_get_u8 (&bits) ? "True" : "False"); - break; - case 6: /* double */ - g_string_append_printf (string, "%g", swfdec_bits_get_double (&bits)); - break; - case 7: /* 32bit int */ - g_string_append_printf (string, "%d", swfdec_bits_get_u32 (&bits)); - break; - case 8: /* 8bit ConstantPool address */ - g_string_append_printf (string, "Pool %u", swfdec_bits_get_u8 (&bits)); - break; - case 9: /* 16bit ConstantPool address */ - g_string_append_printf (string, "Pool %u", swfdec_bits_get_u16 (&bits)); - break; - default: - SWFDEC_ERROR ("Push: type %u not implemented", type); - return JS_FALSE; - } - } - return g_string_free (string, FALSE); -} - -/* NB: constant pool actions are special in that they are called at init time */ -static char * -swfdec_action_print_constant_pool (guint action, const guint8 *data, guint len) -{ - guint i; - GString *string; - SwfdecConstantPool *pool; - - pool = swfdec_constant_pool_new_from_action (data, len); - if (pool == NULL) - return JS_FALSE; - string = g_string_new ("ConstantPool"); - for (i = 0; i < swfdec_constant_pool_size (pool); i++) { - g_string_append (string, i ? ", " : " "); - g_string_append (string, swfdec_constant_pool_get (pool, i)); - g_string_append_printf (string, " (%u)", i); - } - return g_string_free (string, FALSE); -} - -static char * -swfdec_action_print_wait_for_frame2 (guint action, const guint8 *data, guint len) -{ - if (len != 1) { - SWFDEC_ERROR ("WaitForFrame2 needs a 1-byte data"); - return NULL; - } - return g_strdup_printf ("WaitForFrame2 %u", (guint) *data); -} - -static char * -swfdec_action_print_goto_frame2 (guint action, const guint8 *data, guint len) -{ - gboolean play, bias; - SwfdecBits bits; - - swfdec_bits_init_data (&bits, data, len); - if (swfdec_bits_getbits (&bits, 6)) { - SWFDEC_WARNING ("reserved bits in GotoFrame2 aren't 0"); - } - bias = swfdec_bits_getbit (&bits); - play = swfdec_bits_getbit (&bits); - if (bias) { - return g_strdup_printf ("GotoFrame2 %s +%u", play ? "play" : "stop", - swfdec_bits_get_u16 (&bits)); - } else { - return g_strdup_printf ("GotoFrame2 %s", play ? "play" : "stop"); - } -} - -static char * -swfdec_action_print_goto_frame (guint action, const guint8 *data, guint len) -{ - guint frame; - - if (len != 2) - return NULL; - - frame = GUINT16_FROM_LE (*((guint16 *) data)); - return g_strdup_printf ("GotoFrame %u", frame); -} - -static char * -swfdec_action_print_goto_label (guint action, const guint8 *data, guint len) -{ - if (!memchr (data, 0, len)) { - SWFDEC_ERROR ("GotoLabel action does not specify a string"); - return NULL; - } - - return g_strdup_printf ("GotoLabel %s", data); -} - -static char * -swfdec_action_print_wait_for_frame (guint action, const guint8 *data, guint len) -{ - guint frame, jump; - - if (len != 3) - return NULL; - - frame = GUINT16_FROM_LE (*((guint16 *) data)); - jump = data[2]; - return g_strdup_printf ("WaitForFrame %u %u", frame, jump); -} -#endif - -/*** BIG FUNCTION TABLE ***/ - -/* defines minimum and maximum versions for which we have seperate scripts */ -#define MINSCRIPTVERSION 3 -#define MAXSCRIPTVERSION 7 -#define EXTRACT_VERSION(v) MIN ((v) - MINSCRIPTVERSION, MAXSCRIPTVERSION - MINSCRIPTVERSION) - -typedef void (* SwfdecActionExec) (SwfdecAsContext *cx, guint action, const guint8 *data, guint len); -typedef struct { - const char * name; /* name identifying the action */ - char * (* print) (guint action, const guint8 *data, guint len); - int remove; /* values removed from stack or -1 for dynamic */ - int add; /* values added to the stack or -1 for dynamic */ - SwfdecActionExec exec[MAXSCRIPTVERSION - MINSCRIPTVERSION + 1]; - /* array is for version 3, 4, 5, 6, 7+ */ -} SwfdecActionSpec; - -const SwfdecActionSpec actions[256] = { -#if 0 - /* version 3 */ - [0x04] = { "NextFrame", NULL, 0, 0, { swfdec_action_next_frame, swfdec_action_next_frame, swfdec_action_next_frame, swfdec_action_next_frame, swfdec_action_next_frame } }, - [0x05] = { "PreviousFrame", NULL, 0, 0, { swfdec_action_previous_frame, swfdec_action_previous_frame, swfdec_action_previous_frame, swfdec_action_previous_frame, swfdec_action_previous_frame } }, - [0x06] = { "Play", NULL, 0, 0, { swfdec_action_play, swfdec_action_play, swfdec_action_play, swfdec_action_play, swfdec_action_play } }, - [0x07] = { "Stop", NULL, 0, 0, { swfdec_action_stop, swfdec_action_stop, swfdec_action_stop, swfdec_action_stop, swfdec_action_stop } }, - [0x08] = { "ToggleQuality", NULL }, - [0x09] = { "StopSounds", NULL, 0, 0, { swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds, swfdec_action_stop_sounds } }, - /* version 4 */ - [0x0a] = { "Add", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } }, - [0x0b] = { "Subtract", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } }, - [0x0c] = { "Multiply", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } }, - [0x0d] = { "Divide", NULL, 2, 1, { NULL, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary, swfdec_action_binary } }, - [0x0e] = { "Equals", NULL, 2, 1, { NULL, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare } }, - [0x0f] = { "Less", NULL, 2, 1, { NULL, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare, swfdec_action_old_compare } }, - [0x10] = { "And", NULL, 2, 1, { NULL, /* FIXME */NULL, swfdec_action_logical_5, swfdec_action_logical_5, swfdec_action_logical_7 } }, - [0x11] = { "Or", NULL, 2, 1, { NULL, /* FIXME */NULL, swfdec_action_logical_5, swfdec_action_logical_5, swfdec_action_logical_7 } }, - [0x12] = { "Not", NULL, 1, 1, { NULL, swfdec_action_not_4, swfdec_action_not_5, swfdec_action_not_5, swfdec_action_not_5 } }, - [0x13] = { "StringEquals", NULL }, - [0x14] = { "StringLength", NULL }, - [0x15] = { "StringExtract", NULL }, - [0x17] = { "Pop", NULL, 1, 0, { NULL, swfdec_action_pop, swfdec_action_pop, swfdec_action_pop, swfdec_action_pop } }, - [0x18] = { "ToInteger", NULL, 1, 1, { NULL, swfdec_action_to_integer, swfdec_action_to_integer, swfdec_action_to_integer, swfdec_action_to_integer } }, - [0x1c] = { "GetVariable", NULL, 1, 1, { NULL, swfdec_action_get_variable, swfdec_action_get_variable, swfdec_action_get_variable, swfdec_action_get_variable } }, - [0x1d] = { "SetVariable", NULL, 2, 0, { NULL, swfdec_action_set_variable, swfdec_action_set_variable, swfdec_action_set_variable, swfdec_action_set_variable } }, - [0x20] = { "SetTarget2", NULL, 1, 0, { swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2 } }, - [0x21] = { "StringAdd", NULL, 2, 1, { NULL, swfdec_action_string_add, swfdec_action_string_add, swfdec_action_string_add, swfdec_action_string_add } }, - [0x22] = { "GetProperty", NULL, 2, 1, { NULL, swfdec_action_get_property, swfdec_action_get_property, swfdec_action_get_property, swfdec_action_get_property } }, - [0x23] = { "SetProperty", NULL, 3, 0, { NULL, swfdec_action_set_property, swfdec_action_set_property, swfdec_action_set_property, swfdec_action_set_property } }, - [0x24] = { "CloneSprite", NULL }, - [0x25] = { "RemoveSprite", NULL }, - [0x26] = { "Trace", NULL, 1, 0, { NULL, swfdec_action_trace, swfdec_action_trace, swfdec_action_trace, swfdec_action_trace } }, - [0x27] = { "StartDrag", NULL, -1, 0, { NULL, swfdec_action_start_drag, swfdec_action_start_drag, swfdec_action_start_drag, swfdec_action_start_drag } }, - [0x28] = { "EndDrag", NULL, 0, 0, { NULL, swfdec_action_end_drag, swfdec_action_end_drag, swfdec_action_end_drag, swfdec_action_end_drag } }, - [0x29] = { "StringLess", NULL }, - /* version 7 */ - [0x2a] = { "Throw", NULL }, - [0x2b] = { "Cast", NULL }, - [0x2c] = { "Implements", NULL }, - /* version 4 */ - [0x30] = { "RandomNumber", NULL, 1, 1, { NULL, swfdec_action_random_number, swfdec_action_random_number, swfdec_action_random_number, swfdec_action_random_number } }, - [0x31] = { "MBStringLength", NULL }, - [0x32] = { "CharToAscii", NULL }, - [0x33] = { "AsciiToChar", NULL }, - [0x34] = { "GetTime", NULL, 0, 1, { NULL, swfdec_action_get_time, swfdec_action_get_time, swfdec_action_get_time, swfdec_action_get_time } }, - [0x35] = { "MBStringExtract", NULL }, - [0x36] = { "MBCharToAscii", NULL }, - [0x37] = { "MVAsciiToChar", NULL }, - /* version 5 */ - [0x3a] = { "Delete", NULL, 2, 0, { NULL, NULL, swfdec_action_delete, swfdec_action_delete, swfdec_action_delete } }, - [0x3b] = { "Delete2", NULL, 1, 0, { NULL, NULL, swfdec_action_delete2, swfdec_action_delete2, swfdec_action_delete2 } }, - [0x3c] = { "DefineLocal", NULL, 2, 0, { NULL, NULL, swfdec_action_define_local, swfdec_action_define_local, swfdec_action_define_local } }, - [0x3d] = { "CallFunction", NULL, -1, 1, { NULL, NULL, swfdec_action_call_function, swfdec_action_call_function, swfdec_action_call_function } }, - [0x3e] = { "Return", NULL, 1, 0, { NULL, NULL, swfdec_action_return, swfdec_action_return, swfdec_action_return } }, - [0x3f] = { "Modulo", NULL, 2, 1, { NULL, NULL, swfdec_action_modulo_5, swfdec_action_modulo_5, swfdec_action_modulo_7 } }, - [0x40] = { "NewObject", NULL, -1, 1, { NULL, NULL, swfdec_action_new_object, swfdec_action_new_object, swfdec_action_new_object } }, - [0x41] = { "DefineLocal2", NULL, 1, 0, { NULL, NULL, swfdec_action_define_local2, swfdec_action_define_local2, swfdec_action_define_local2 } }, - [0x42] = { "InitArray", NULL, -1, 1, { NULL, NULL, swfdec_action_init_array, swfdec_action_init_array, swfdec_action_init_array } }, - [0x43] = { "InitObject", NULL, -1, 1, { NULL, NULL, swfdec_action_init_object, swfdec_action_init_object, swfdec_action_init_object } }, - [0x44] = { "TypeOf", NULL, 1, 1, { NULL, NULL, swfdec_action_type_of, swfdec_action_type_of, swfdec_action_type_of } }, - [0x45] = { "TargetPath", NULL, 1, 1, { NULL, NULL, swfdec_action_target_path, swfdec_action_target_path, swfdec_action_target_path } }, - [0x46] = { "Enumerate", NULL }, - [0x47] = { "Add2", NULL, 2, 1, { NULL, NULL, swfdec_action_add2_5, swfdec_action_add2_5, swfdec_action_add2_7 } }, - [0x48] = { "Less2", NULL, 2, 1, { NULL, NULL, swfdec_action_new_comparison_6, swfdec_action_new_comparison_6, swfdec_action_new_comparison_7 } }, - [0x49] = { "Equals2", NULL, 2, 1, { NULL, NULL, swfdec_action_equals2, swfdec_action_equals2, swfdec_action_equals2 } }, - [0x4a] = { "ToNumber", NULL, 1, 1, { NULL, NULL, swfdec_action_to_number, swfdec_action_to_number, swfdec_action_to_number } }, - [0x4b] = { "ToString", NULL, 1, 1, { NULL, NULL, swfdec_action_to_string, swfdec_action_to_string, swfdec_action_to_string } }, - [0x4c] = { "PushDuplicate", NULL, 1, 2, { NULL, NULL, swfdec_action_push_duplicate, swfdec_action_push_duplicate, swfdec_action_push_duplicate } }, - [0x4d] = { "Swap", NULL, 2, 2, { NULL, NULL, swfdec_action_swap, swfdec_action_swap, swfdec_action_swap } }, - [0x4e] = { "GetMember", NULL, 2, 1, { NULL, swfdec_action_get_member, swfdec_action_get_member, swfdec_action_get_member, swfdec_action_get_member } }, - [0x4f] = { "SetMember", NULL, 3, 0, { NULL, swfdec_action_set_member, swfdec_action_set_member, swfdec_action_set_member, swfdec_action_set_member } }, - [0x50] = { "Increment", NULL, 1, 1, { NULL, NULL, swfdec_action_increment, swfdec_action_increment, swfdec_action_increment } }, - [0x51] = { "Decrement", NULL, 1, 1, { NULL, NULL, swfdec_action_decrement, swfdec_action_decrement, swfdec_action_decrement } }, - [0x52] = { "CallMethod", NULL, -1, 1, { NULL, NULL, swfdec_action_call_method, swfdec_action_call_method, swfdec_action_call_method } }, - [0x53] = { "NewMethod", NULL, -1, 1, { NULL, NULL, swfdec_action_new_method, swfdec_action_new_method, swfdec_action_new_method } }, - /* version 6 */ - [0x54] = { "InstanceOf", NULL }, - [0x55] = { "Enumerate2", NULL, 1, -1, { NULL, NULL, NULL, swfdec_action_enumerate2, swfdec_action_enumerate2 } }, - /* version 5 */ - [0x60] = { "BitAnd", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } }, - [0x61] = { "BitOr", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } }, - [0x62] = { "BitXor", NULL, 2, 1, { NULL, NULL, swfdec_action_bitwise, swfdec_action_bitwise, swfdec_action_bitwise } }, - [0x63] = { "BitLShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } }, - [0x64] = { "BitRShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } }, - [0x65] = { "BitURShift", NULL, 2, 1, { NULL, NULL, swfdec_action_shift, swfdec_action_shift, swfdec_action_shift } }, - /* version 6 */ - [0x66] = { "StrictEquals", NULL }, - [0x67] = { "Greater", NULL, 2, 1, { NULL, NULL, NULL, swfdec_action_new_comparison_6, swfdec_action_new_comparison_7 } }, - [0x68] = { "StringGreater", NULL }, - /* version 7 */ - [0x69] = { "Extends", NULL, 2, 0, { NULL, NULL, NULL, NULL, swfdec_action_extends } }, - - /* version 3 */ - [0x81] = { "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 } }, - [0x83] = { "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 } }, - /* version 5 */ - [0x87] = { "StoreRegister", swfdec_action_print_store_register, 1, 1, { NULL, NULL, swfdec_action_store_register, swfdec_action_store_register, swfdec_action_store_register } }, - [0x88] = { "ConstantPool", swfdec_action_print_constant_pool, 0, 0, { NULL, NULL, swfdec_action_constant_pool, swfdec_action_constant_pool, swfdec_action_constant_pool } }, - /* version 3 */ - [0x8a] = { "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 } }, - [0x8b] = { "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 } }, - [0x8c] = { "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 } }, - /* version 4 */ - [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 } }, - /* version 7 */ - [0x8e] = { "DefineFunction2", swfdec_action_print_define_function, 0, -1, { NULL, NULL, NULL, swfdec_action_define_function, swfdec_action_define_function } }, - [0x8f] = { "Try", NULL }, - /* version 5 */ - [0x94] = { "With", NULL }, - /* version 4 */ - [0x96] = { "Push", swfdec_action_print_push, 0, -1, { NULL, swfdec_action_push, swfdec_action_push, swfdec_action_push, swfdec_action_push } }, - [0x99] = { "Jump", swfdec_action_print_jump, 0, 0, { NULL, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump } }, - [0x9a] = { "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 } }, - /* version 5 */ - [0x9b] = { "DefineFunction", swfdec_action_print_define_function, 0, -1, { NULL, NULL, swfdec_action_define_function, swfdec_action_define_function, swfdec_action_define_function } }, - /* version 4 */ - [0x9d] = { "If", swfdec_action_print_if, 1, 0, { NULL, swfdec_action_if, swfdec_action_if, swfdec_action_if, swfdec_action_if } }, - [0x9e] = { "Call", NULL }, - [0x9f] = { "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 } } -#endif -}; - char * swfdec_script_print_action (guint action, const guint8 *data, guint len) { - const SwfdecActionSpec *spec = actions + action; + const SwfdecActionSpec *spec = swfdec_as_actions + action; if (action & 0x80) { if (spec->print == NULL) { @@ -2649,14 +215,13 @@ static gboolean validate_action (gconstpointer bytecode, guint action, const guint8 *data, guint len, gpointer scriptp) { SwfdecScript *script = scriptp; - int version = EXTRACT_VERSION (script->version); + int version = SWFDEC_AS_EXTRACT_SCRIPT_VERSION (script->version); - /* ensure there's a function to execute this opcode, otherwise fail */ - if (actions[action].exec[version] == NULL) { - SWFDEC_ERROR ("no function for %u %s in v%u", action, - actions[action].name ? actions[action].name : "Unknown", + /* warn if there's no function to execute this opcode */ + if (swfdec_as_actions[action].exec[version] == NULL) { + SWFDEC_ERROR ("no function for %3u 0x%02X %s in v%u", action, action, + swfdec_as_actions[action].name ? swfdec_as_actions[action].name : "Unknown", script->version); - return FALSE; } /* we might want to do stuff here for certain actions */ #if 0 @@ -2693,7 +258,7 @@ swfdec_script_new (SwfdecBits *bits, con g_return_val_if_fail (bits != NULL, NULL); - if (version < MINSCRIPTVERSION) { + if (version < SWFDEC_AS_MIN_SCRIPT_VERSION) { SWFDEC_ERROR ("swfdec version %u doesn't support scripts", version); return NULL; } @@ -2754,340 +319,6 @@ swfdec_script_unref (SwfdecScript *scrip g_free (script); } -#if 0 -JSBool -swfdec_script_interpret (SwfdecScript *script, JSContext *cx, jsval *rval) -{ - JSStackFrame *fp; - guint8 *startpc, *pc, *endpc, *nextpc; - JSBool ok = JS_TRUE; - void *mark; - jsval *startsp; -#ifndef G_DISABLE_ASSERT - jsval *checksp; -#endif - int stack_check; - guint action, len; - guint8 *data; - guint version; - const SwfdecActionSpec *spec; - - /* set up general stuff */ - swfdec_script_ref (script); - version = EXTRACT_VERSION (script->version); - *rval = JSVAL_VOID; - fp = cx->fp; - /* do the preloading */ - if (script->preloads) { - guint i; - for (i = 0; i < fp->fun->nargs; i++) { - if (script->preloads[i]) - fp->vars[script->preloads[i]] = fp->argv[i]; - } - } - if (script->flags) { - guint preload_reg = 1; - SwfdecPlayer *player = JS_GetContextPrivate (cx); - if (script->flags & SWFDEC_SCRIPT_PRELOAD_THIS) - fp->vars[preload_reg++] = OBJECT_TO_JSVAL (fp->thisp); - if (script->flags & SWFDEC_SCRIPT_PRELOAD_ARGS) { - if (!JS_GetProperty (cx, fp->scopeChain, "arguments", &fp->vars[preload_reg++])) { - ok = JS_FALSE; - goto out; - } - } - if (script->flags & SWFDEC_SCRIPT_SUPPRESS_ARGS) { - /* FIXME: keep in sync with jsfun.c */ - fp->flags |= JS_BIT (JSFRAME_OVERRIDE_SHIFT); - } - if (script->flags & SWFDEC_SCRIPT_PRELOAD_SUPER) { - if (!JS_GetProperty (cx, JS_GetPrototype (cx, fp->thisp), - fp->flags & JSINVOKE_CONSTRUCT ? "__constructor__" : "__proto__", - &fp->vars[preload_reg++])) { - ok = JS_FALSE; - goto out; - } - } - if (script->flags & SWFDEC_SCRIPT_PRELOAD_ROOT) { - JSAtom *atom; - JSObject *obj, *pobj; - JSProperty *prop; - SWFDEC_LOG ("preloading root into register %u", preload_reg); - if (!(atom = js_Atomize (cx, "_root", 5, 0)) || - !js_FindProperty (cx, (jsid) atom, &obj, &pobj, &prop) || - !js_GetProperty (cx, obj, (jsid) atom, &fp->vars[preload_reg++])) { - ok = JS_FALSE; - goto out; - } - } - if (script->flags & SWFDEC_SCRIPT_PRELOAD_PARENT) { - JSAtom *atom; - JSObject *obj, *pobj; - JSProperty *prop; - SWFDEC_LOG ("preloading parent into register %u", preload_reg); - if (!(atom = js_Atomize (cx, "_parent", 7, 0)) || - !js_FindProperty (cx, (jsid) atom, &obj, &pobj, &prop) || - !js_GetProperty (cx, obj, (jsid) atom, &fp->vars[preload_reg++])) { - ok = JS_FALSE; - goto out; - } - } - if (script->flags & SWFDEC_SCRIPT_PRELOAD_GLOBAL) - fp->vars[preload_reg++] = OBJECT_TO_JSVAL (player->jsobj); - } - /* set up the script */ - startpc = pc = script->buffer->data; - endpc = startpc + script->buffer->length; - fp->pc = pc; - /* set up stack */ - startsp = js_AllocRawStack (cx, STACKSIZE, &mark); - if (!startsp) { - ok = JS_FALSE; - goto out; - } - fp->spbase = startsp; - fp->spend = startsp + STACKSIZE; - fp->sp = startsp; - /* Check for too much nesting, or too deep a C stack. */ - if (++cx->interpLevel == MAX_INTERP_LEVEL || - !JS_CHECK_STACK_SIZE(cx, stack_check)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - ok = JS_FALSE; - goto out; - } - /* initialize the constant pool */ - if (script->constant_pool) { - spec = actions + 0x88; - ok = spec->exec[version] (cx, 0x88, script->constant_pool->data + 3, - script->constant_pool->length - 3); - if (!ok) { - SWFDEC_WARNING ("Constant pool initialization failed"); - goto out; - } - } - - while (TRUE) { - /* check pc */ - if (pc == endpc) { - /* scripts created via DefineFunction or the Return action use this way out */ - *rval = fp->rval; - break; - } - if (pc < startpc || pc >= endpc) { - SWFDEC_ERROR ("pc %p not in valid range [%p, %p) anymore", pc, startpc, endpc); - goto internal_error; - } - - /* run interrupt handler */ - if (cx->runtime->interruptHandler) { - jsval tmp; - switch (cx->runtime->interruptHandler (cx, NULL, pc, &tmp, - cx->runtime->interruptHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = tmp; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = tmp; - ok = JS_FALSE; - goto out; - default: - g_assert_not_reached (); - break; - } - } - - /* decode next action */ - action = *pc; - spec = actions + action; - if (action == 0) - break; - if (action & 0x80) { - if (pc + 2 >= endpc) { - SWFDEC_ERROR ("action %u length value out of range", action); - goto internal_error; - } - data = pc + 3; - len = pc[1] | pc[2] << 8; - if (data + len > endpc) { - SWFDEC_ERROR ("action %u length %u out of range", action, len); - goto internal_error; - } - nextpc = pc + 3 + len; - } else { - data = NULL; - len = 0; - nextpc = pc + 1; - } - /* check action is valid */ - if (spec->exec[version] == NULL) { - SWFDEC_ERROR ("cannot interpret action %u %s for version %u", action, - spec->name ? spec->name : "Unknown", script->version); - goto internal_error; - } - if (spec->remove > 0 && - !swfdec_script_ensure_stack (cx, spec->remove)) { - ok = JS_FALSE; - goto out; - } - if (spec->add > 0 && - fp->sp + spec->add - MAX (spec->remove, 0) > fp->spend) { - SWFDEC_ERROR ("FIXME: implement stack expansion, we got an overflow"); - goto internal_error; - } -#ifndef G_DISABLE_ASSERT - checksp = (spec->add >= 0 && spec->remove >= 0) ? fp->sp + spec->add - spec->remove : NULL; -#endif - ok = spec->exec[version] (cx, action, data, len); - if (!ok) { - SWFDEC_WARNING ("action %s failed", spec->name); - goto out; - } -#ifndef G_DISABLE_ASSERT - if (checksp != NULL && checksp != fp->sp) { - /* check stack was handled like expected */ - g_error ("action %s was supposed to change the stack by %d (+%d -%d), but it changed by %td", - spec->name, spec->add - spec->remove, spec->add, spec->remove, - fp->sp - checksp + spec->add - spec->remove); - } -#endif - if (fp->pc == pc) { - fp->pc = pc = nextpc; - } else { - pc = fp->pc; - } - } - -out: -#if 0 - /* FIXME: exception handling */ - if (!ok && cx->throwing) { - /* - * Look for a try block within this frame that can catch the exception. - */ - SCRIPT_FIND_CATCH_START(script, pc, pc); - if (pc) { - len = 0; - cx->throwing = JS_FALSE; /* caught */ - ok = JS_TRUE; - goto advance_pc; - } - } -#endif -no_catch: - - /* Reset sp before freeing stack slots, because our caller may GC soon. */ - fp->sp = fp->spbase; - fp->spbase = NULL; - if (fp->constant_pool) { - swfdec_constant_pool_free (fp->constant_pool); - fp->constant_pool = NULL; - } - js_FreeRawStack(cx, mark); - cx->interpLevel--; - swfdec_script_unref (script); - return ok; - -internal_error: - *rval = JSVAL_VOID; - ok = JS_TRUE; - goto no_catch; -} - -/** - * swfdec_script_execute: - * @script: a #SwfdecScript to execute - * @scriptable: #SwfdecScriptable to use as this in script scope - * - * Executes @script in the context of @scriptable. No local scope will be - * added, so no local variables can exist. As per Actionscript, 4 registers - * will be created. - * - * Returns: the return value of @script - **/ -#endif -void -swfdec_script_execute (SwfdecScript *script, SwfdecScriptable *scriptable) -{ -} -#if 0 - JSContext *cx; - JSStackFrame *oldfp, frame; - JSObject *obj; - JSBool ok; - void *mark; - - g_return_val_if_fail (script != NULL, JSVAL_VOID); - g_return_val_if_fail (SWFDEC_IS_SCRIPTABLE (scriptable), JSVAL_VOID); - - cx = scriptable->jscx; - obj = swfdec_scriptable_get_object (scriptable); - if (obj == NULL) - return JSVAL_VOID; - oldfp = cx->fp; - - frame.callobj = NULL; - frame.script = NULL; - frame.argsobj = NULL; - frame.fun = swfdec_script_ensure_function (script, scriptable); - frame.swf = script; - frame.constant_pool = NULL; - frame.thisp = obj; - frame.argc = 0; - frame.argv = NULL; - frame.annotation = NULL; - frame.sharpArray = NULL; - frame.rval = JSVAL_VOID; - frame.down = NULL; - frame.pc = NULL; - frame.sp = oldfp ? oldfp->sp : NULL; - frame.spbase = NULL; - frame.sharpDepth = 0; - frame.flags = 0; - frame.dormantNext = NULL; - frame.objAtomMap = NULL; - /* no local scope here */ - frame.scopeChain = obj; - frame.varobj = obj; - /* allocate stack for variables */ - frame.nvars = script->n_registers; - frame.vars = js_AllocRawStack (cx, frame.nvars, &mark); - if (frame.vars == NULL) { - return JS_FALSE; - } - frame.vars[0] = frame.vars[1] = frame.vars[2] = frame.vars[3] = JSVAL_VOID; - - if (oldfp) { - g_assert (!oldfp->dormantNext); - oldfp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = oldfp; - } - cx->fp = &frame; - - /* - * Use frame.rval, not result, so the last result stays rooted across any - * GC activations nested within this js_Interpret. - */ - ok = swfdec_script_interpret (script, cx, &frame.rval); - - js_FreeRawStack (cx, mark); - - cx->fp = oldfp; - if (oldfp) { - g_assert (cx->dormantFrameChain == oldfp); - cx->dormantFrameChain = oldfp->dormantNext; - oldfp->dormantNext = NULL; - } - - return ok ? frame.rval : JSVAL_VOID; -} -#endif - /*** UTILITY FUNCTIONS ***/ const char * @@ -3095,7 +326,7 @@ swfdec_action_get_name (guint action) { g_return_val_if_fail (action < 256, NULL); - return actions[action].name; + return swfdec_as_actions[action].name; } guint @@ -3106,7 +337,7 @@ swfdec_action_get_from_name (const char g_return_val_if_fail (name != NULL, 0); for (i = 0; i < 256; i++) { - if (actions[i].name && g_str_equal (name, actions[i].name)) + if (swfdec_as_actions[i].name && g_str_equal (name, swfdec_as_actions[i].name)) return i; } return 0; diff --git a/libswfdec/swfdec_script.h b/libswfdec/swfdec_script.h index 673332a..0a3d966 100644 --- a/libswfdec/swfdec_script.h +++ b/libswfdec/swfdec_script.h @@ -66,10 +66,12 @@ guint swfdec_action_get_from_name (cons SwfdecConstantPool * swfdec_constant_pool_new_from_action (const guint8 * data, guint len); +void swfdec_constant_pool_free (SwfdecConstantPool * pool); guint swfdec_constant_pool_size (SwfdecConstantPool * pool); const char * swfdec_constant_pool_get (SwfdecConstantPool * pool, guint i); -void swfdec_constant_pool_free (SwfdecConstantPool * pool); +SwfdecBuffer * swfdec_constant_pool_get_area (SwfdecScript * script, + SwfdecConstantPool * pool); SwfdecScript * swfdec_script_new (SwfdecBits * bits, const char * name, diff-tree ee1c84df7a50b023262001feb21c90380798161a (from d7ed0cb12ed1506e74bd766fd0e88c66910f0f7f) Author: Benjamin Otte <otte@gnome.org> Date: Wed Apr 4 14:47:36 2007 +0200 implement some more of swfdec_as_value_to_string diff --git a/libswfdec/swfdec_as_types.c b/libswfdec/swfdec_as_types.c index 12f0d44..14cb0e4 100644 --- a/libswfdec/swfdec_as_types.c +++ b/libswfdec/swfdec_as_types.c @@ -62,18 +62,55 @@ const char *swfdec_as_strings[] = { SWFDEC_AS_CONSTANT_STRING ("NetStream.Play.Start"), SWFDEC_AS_CONSTANT_STRING ("NetStream.Play.Stop"), SWFDEC_AS_CONSTANT_STRING ("NetStream.Play.StreamNotFound"), + SWFDEC_AS_CONSTANT_STRING ("undefined"), + SWFDEC_AS_CONSTANT_STRING ("null"), + SWFDEC_AS_CONSTANT_STRING ("[object Object]"), + SWFDEC_AS_CONSTANT_STRING ("true"), + SWFDEC_AS_CONSTANT_STRING ("false"), /* add more here */ NULL }; +/** + * swfdec_as_value_to_string: + * @context: a #SwfdecAsContext + * @value: value to be expressed as string + * + * Converts @value to a string. + * <warning>This function may run the garbage collector.</warning> + * + * Returns: a garbage-collected string representing @value + **/ const char * swfdec_as_value_to_string (SwfdecAsContext *context, const SwfdecAsValue *value) { g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), SWFDEC_AS_STR_EMPTY); g_return_val_if_fail (SWFDEC_IS_AS_VALUE (value), SWFDEC_AS_STR_EMPTY); - if (SWFDEC_AS_VALUE_IS_STRING (value)) { - return SWFDEC_AS_VALUE_GET_STRING (value); + switch (value->type) { + case SWFDEC_TYPE_AS_STRING: + return SWFDEC_AS_VALUE_GET_STRING (value); + case SWFDEC_TYPE_AS_UNDEFINED: + if (context->version > 6) + return SWFDEC_AS_STR_UNDEFINED; + else + return SWFDEC_AS_STR_EMPTY; + case SWFDEC_TYPE_AS_BOOLEAN: + return SWFDEC_AS_VALUE_GET_BOOLEAN (value) ? SWFDEC_AS_STR_TRUE : SWFDEC_AS_STR_FALSE; + case SWFDEC_TYPE_AS_NULL: + return SWFDEC_AS_STR_NULL; + case SWFDEC_TYPE_AS_NUMBER: + { + char *s = g_strdup_printf ("%g", SWFDEC_AS_VALUE_GET_NUMBER (value)); + const char *ret = swfdec_as_context_get_string (context, s); + g_free (s); + return ret; + } + case SWFDEC_TYPE_AS_ASOBJECT: + /* FIXME! */ + return SWFDEC_AS_STR_OBJECT_OBJECT; + default: + g_assert_not_reached (); + return SWFDEC_AS_STR_EMPTY; } - g_assert_not_reached (); } diff --git a/libswfdec/swfdec_as_types.h b/libswfdec/swfdec_as_types.h index 92a7355..533df03 100644 --- a/libswfdec/swfdec_as_types.h +++ b/libswfdec/swfdec_as_types.h @@ -125,6 +125,11 @@ extern const char *swfdec_as_strings[]; #define SWFDEC_AS_STR_NETSTREAM_PLAY_START (swfdec_as_strings[31] + 1) #define SWFDEC_AS_STR_NETSTREAM_PLAY_STOP (swfdec_as_strings[32] + 1) #define SWFDEC_AS_STR_NETSTREAM_PLAY_STREAMNOTFOUND (swfdec_as_strings[33] + 1) +#define SWFDEC_AS_STR_UNDEFINED (swfdec_as_strings[34] + 1) +#define SWFDEC_AS_STR_NULL (swfdec_as_strings[35] + 1) +#define SWFDEC_AS_STR_OBJECT_OBJECT (swfdec_as_strings[36] + 1) +#define SWFDEC_AS_STR_TRUE (swfdec_as_strings[37] + 1) +#define SWFDEC_AS_STR_FALSE (swfdec_as_strings[38] + 1) /* all existing actions */ typedef enum { diff-tree d7ed0cb12ed1506e74bd766fd0e88c66910f0f7f (from 4acdbf33dfad211c233f81a51f70d25e42676fc0) Author: Benjamin Otte <otte@gnome.org> Date: Wed Apr 4 14:46:58 2007 +0200 implement swfdec_as_object_run diff --git a/libswfdec/swfdec_as_object.c b/libswfdec/swfdec_as_object.c index 6f5ac61..a8cb206 100644 --- a/libswfdec/swfdec_as_object.c +++ b/libswfdec/swfdec_as_object.c @@ -23,6 +23,7 @@ #include "swfdec_as_object.h" #include "swfdec_as_context.h" +#include "swfdec_as_frame.h" #include "swfdec_as_function.h" #include "swfdec_debug.h" @@ -263,8 +264,8 @@ swfdec_as_object_run (SwfdecAsObject *ob g_return_if_fail (SWFDEC_AS_OBJECT_HAS_CONTEXT (object)); g_return_if_fail (script != NULL); - g_assert_not_reached (); - swfdec_as_context_run (object->context); + if (swfdec_as_frame_new (object, script)) + swfdec_as_context_run (object->context); } void diff-tree 4acdbf33dfad211c233f81a51f70d25e42676fc0 (from df9459902a3d4cc821233ce7a973c1284cb4ffcf) Author: Benjamin Otte <otte@gnome.org> Date: Wed Apr 4 14:46:21 2007 +0200 remember the number of registers diff --git a/libswfdec/swfdec_as_frame.c b/libswfdec/swfdec_as_frame.c index 8abf677..ca2d201 100644 --- a/libswfdec/swfdec_as_frame.c +++ b/libswfdec/swfdec_as_frame.c @@ -93,6 +93,7 @@ swfdec_as_frame_new (SwfdecAsObject *thi if (!swfdec_as_context_use_mem (context, size)) return NULL; frame = g_object_new (SWFDEC_TYPE_AS_FRAME, NULL); + SWFDEC_DEBUG ("new frame"); swfdec_as_object_add (SWFDEC_AS_OBJECT (frame), context, size); frame->next = context->frame; context->frame = frame; @@ -101,7 +102,8 @@ swfdec_as_frame_new (SwfdecAsObject *thi frame->stack = stack; frame->scope = thisp; frame->var_object = thisp; - frame->registers = g_slice_alloc0 (sizeof (SwfdecAsValue) * script->n_registers); + frame->n_registers = script->n_registers; + frame->registers = g_slice_alloc0 (sizeof (SwfdecAsValue) * frame->n_registers); SWFDEC_AS_VALUE_SET_OBJECT (&val, thisp); swfdec_as_object_set (SWFDEC_AS_OBJECT (frame), SWFDEC_AS_STR_THIS, &val); return frame;
Seemingly Similar Threads
- Branch 'interpreter' - 18 commits - libswfdec/swfdec_image.c libswfdec/swfdec_image.h libswfdec/swfdec_js.c libswfdec/swfdec_js_color.c libswfdec/swfdec_js_sound.c libswfdec/swfdec_pattern.c libswfdec/swfdec_scriptable.c libswfdec/swfdec_script.c
- 109 commits - configure.ac libswfdec/js libswfdec/Makefile.am libswfdec/swfdec_bits.c libswfdec/swfdec_bits.h libswfdec/swfdec_buffer.c libswfdec/swfdec_button_movie.c libswfdec/swfdec_codec_screen.c libswfdec/swfdec_color.c libswfdec/swfdec_color.h
- 17 commits - libswfdec/js libswfdec/swfdec_js.c libswfdec/swfdec_js_global.c libswfdec/swfdec_js.h libswfdec/swfdec_js_movie.c libswfdec/swfdec_movie.c libswfdec/swfdec_player.c libswfdec/swfdec_player_internal.h libswfdec/swfdec_root_movie.c
- Branch 'interpreter' - 28 commits - configure.ac libswfdec/js libswfdec/swfdec_buffer.c libswfdec/swfdec_edittext_movie.c libswfdec/swfdec_js.c libswfdec/swfdec_js_global.c libswfdec/swfdec_js.h libswfdec/swfdec_js_movie.c libswfdec/swfdec_player.c
- Branch 'interpreter' - 9 commits - libswfdec/js libswfdec/swfdec_script.c libswfdec/swfdec_script.h player/swfdec_debug_stack.c test/trace