📄 swfdec_script.c
字号:
/* 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_script.h"#include "swfdec_debug.h"#include "swfdec_debugger.h"#include "swfdec_scriptable.h"#include "js/jscntxt.h"#include "js/jsinterp.h"#include <errno.h>#include <math.h>#include <string.h>#include "swfdec_decoder.h"#include "swfdec_js.h"#include "swfdec_movie.h"#include "swfdec_player_internal.h"#include "swfdec_root_movie.h"#include "swfdec_sprite.h"#include "swfdec_sprite_movie.h"#include "js/jsfun.h"#include "js/jsscope.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/*** CONSTANT POOLS ***/SwfdecConstantPool *swfdec_constant_pool_new_from_action (const guint8 *data, guint len){ guint8 *next; guint i, n; GPtrArray *pool; if (len < 2) { SWFDEC_ERROR ("constant pool too small"); return NULL; } n = GUINT16_FROM_LE (*((guint16*) data)); data += 2; len -= 2; pool = g_ptr_array_sized_new (n); g_ptr_array_set_size (pool, n); for (i = 0; i < n; i++) { next = memchr (data, 0, len); if (next == NULL) { SWFDEC_ERROR ("not enough strings available"); g_ptr_array_free (pool, TRUE); return NULL; } next++; g_ptr_array_index (pool, i) = (gpointer) data; len -= next - data; data = next; } if (len != 0) { SWFDEC_WARNING ("constant pool didn't consume whole buffer (%u bytes leftover)", len); } return pool;}guintswfdec_constant_pool_size (SwfdecConstantPool *pool){ return pool->len;}const char *swfdec_constant_pool_get (SwfdecConstantPool *pool, guint i){ g_assert (i < pool->len); return g_ptr_array_index (pool, i);}voidswfdec_constant_pool_free (SwfdecConstantPool *pool){ g_ptr_array_free (pool, TRUE);}/* FIXME: this is a bit hacky */static SwfdecBuffer *swfdec_constant_pool_get_area (SwfdecScript *script, SwfdecConstantPool *pool){ guint8 *start; SwfdecBuffer *buffer; guint len; if (pool->len == 0) return NULL; start = (guint8 *) g_ptr_array_index (pool, 0) - 5; buffer = script->buffer; if (start < buffer->data) { /* DefineFunction inside DefineFunction */ g_assert (buffer->parent != NULL); buffer = buffer->parent; g_assert (start >= buffer->data); } g_assert (start + 3 < buffer->data + buffer->length); g_assert (*start == 0x88); len = 3 + (start[1] | start[2] << 8); g_assert (start + len < buffer->data + buffer->length); return swfdec_buffer_new_subbuffer (buffer, start - buffer->data, len);}/*** SUPPORT FUNCTIONS ***/static voidswfdec_script_add_to_player (SwfdecScript *script, SwfdecPlayer *player){ if (SWFDEC_IS_DEBUGGER (player)) { swfdec_debugger_add_script (SWFDEC_DEBUGGER (player), script); script->debugger = player; }}/** * swfdec_script_ensure_stack: * @cx: #JSContext to check * @n_elements: number of elements the stack should contain * * Ensures that the stack is at least @n_elements values. If not enough stack * space is available, the stack is filled up with JSVAL_VOID. * * Returns: JS_TRUE on success or JS_FALSE on OOM **/static inline JSBoolswfdec_script_ensure_stack (JSContext *cx, guint n_elements){ JSStackFrame *fp = cx->fp; guint current = (guint) (fp->sp - fp->spbase); if (current >= n_elements) return JS_TRUE; if (n_elements > (guint) (fp->spend - fp->spbase)) { SWFDEC_ERROR ("FIXME: implement stack expansion, we got an overflow (want %u, have %td)", n_elements, (fp->spend - fp->spbase)); return JS_FALSE; } if (current) { n_elements -= current; memmove (fp->spbase + n_elements, fp->spbase, (fp->sp - fp->spbase) * sizeof (jsval)); } fp->sp += n_elements; while (n_elements) { n_elements--; fp->spbase[n_elements] = JSVAL_VOID; } return JS_TRUE;}static gbooleanswfdec_action_has_register (JSContext *cx, guint i){ if (cx->fp->fun == NULL) return i < 4; else return i < cx->fp->fun->nvars;}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 JSBoolswfdec_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 JSBoolswfdec_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 JSBoolswfdec_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 doubleswfdec_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 JSBoolswfdec_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 JSBoolswfdec_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 JSBoolswfdec_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 JSBoolswfdec_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 JSBoolswfdec_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 JSBoolswfdec_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 JSBoolswfdec_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 intswfdec_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 JSBoolswfdec_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 voidswfdec_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 JSBoolswfdec_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];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -