[Swfdec] Branch 'interpreter' - 20 commits - autogen.sh configure.ac libswfdec/js libswfdec/swfdec_debug.h libswfdec/swfdec_js.c libswfdec/swfdec_js_color.c libswfdec/swfdec_js_movie.c libswfdec/swfdec_movie.c libswfdec/swfdec_movie.h libswfdec/swfdec_script.c player/.gitignore player/Makefile.am player/swfdec_playback_alsa.c player/swfdec_playback.c player/swfdec_playback_none.c test/trace

Benjamin Otte company at kemper.freedesktop.org
Wed Jan 31 07:27:38 PST 2007


 autogen.sh                     |    2 
 configure.ac                   |   39 +++
 libswfdec/js/jsfun.c           |   11 -
 libswfdec/js/jsinterp.c        |    5 
 libswfdec/js/jsobj.c           |    2 
 libswfdec/swfdec_debug.h       |    2 
 libswfdec/swfdec_js.c          |    4 
 libswfdec/swfdec_js_color.c    |   33 +--
 libswfdec/swfdec_js_movie.c    |   25 --
 libswfdec/swfdec_movie.c       |   24 ++
 libswfdec/swfdec_movie.h       |    1 
 libswfdec/swfdec_script.c      |  436 +++++++++++++++++++++++++++++++++++++----
 player/.gitignore              |    1 
 player/Makefile.am             |   28 +-
 player/swfdec_playback_none.c  |   38 +++
 test/trace/Makefile.am         |    6 
 test/trace/color-new.swf       |binary
 test/trace/color-new.swf.trace |   10 
 test/trace/function1.swf       |binary
 test/trace/function1.swf.trace |   50 ++++
 test/trace/function2.swf       |binary
 test/trace/function2.swf.trace |    2 
 22 files changed, 621 insertions(+), 98 deletions(-)

New commits:
diff-tree 9335e136a9a1f2f3dbbbda7a7b156b55d94e7c4c (from 90bd9323aab5388329dfac37a165bd421818875c)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 16:28:09 2007 +0100

    handle colors not referring to movies
    
    This also fixes the color-new test

diff --git a/libswfdec/swfdec_js_color.c b/libswfdec/swfdec_js_color.c
index ade8d6a..095abd7 100644
--- a/libswfdec/swfdec_js_color.c
+++ b/libswfdec/swfdec_js_color.c
@@ -32,7 +32,8 @@ swfdec_js_color_get_rgb (JSContext *cx, 
   int result;
   SwfdecMovie *movie = JS_GetPrivate (cx, obj);
 
-  g_assert (movie);
+  if (!movie)
+    return JS_TRUE;
   result = (movie->color_transform.rb << 16) |
 	   ((movie->color_transform.gb % 256) << 8) | 
 	   (movie->color_transform.bb % 256);
@@ -55,7 +56,8 @@ swfdec_js_color_get_transform (JSContext
   JSObject *ret;
   SwfdecMovie *movie = JS_GetPrivate (cx, obj);
 
-  g_assert (movie);
+  if (!movie)
+    return JS_TRUE;
   ret = JS_NewObject (cx, NULL, NULL, NULL);
   if (ret == NULL)
     return JS_TRUE;
@@ -78,7 +80,8 @@ swfdec_js_color_set_rgb (JSContext *cx, 
   unsigned int color;
   SwfdecMovie *movie = JS_GetPrivate (cx, obj);
 
-  g_assert (movie);
+  if (!movie)
+    return JS_TRUE;
   if (!JS_ValueToECMAUint32 (cx, argv[0], &color))
     return JS_TRUE;
 
@@ -117,8 +120,9 @@ swfdec_js_color_set_transform (JSContext
   JSObject *parse;
   SwfdecMovie *movie = JS_GetPrivate (cx, obj);
 
-  g_assert (movie);
-  if (!JSVAL_IS_OBJECT (argv[0]))
+  if (!movie)
+    return JS_TRUE;
+  if (!movie)
     return JS_TRUE;
   parse = JSVAL_TO_OBJECT (argv[0]);
   parse_property (cx, parse, "ra", &movie->color_transform.ra, TRUE);
@@ -161,7 +165,6 @@ swfdec_js_color_finalize (JSContext *cx,
   SwfdecMovie *movie;
 
   movie = JS_GetPrivate (cx, obj);
-  /* since we also finalize the class, not everyone has a private object */
   if (movie) {
     g_object_unref (movie);
   }
@@ -179,14 +182,16 @@ swfdec_js_color_new (JSContext *cx, JSOb
 {
   SwfdecMovie *movie;
 
-  movie = swfdec_scriptable_from_jsval (cx, argv[0], SWFDEC_TYPE_MOVIE);
-  if (movie == NULL) {
-    SWFDEC_INFO ("attempted to construct a color without a movie");
-    return JS_TRUE;
+  if (argc > 0) {
+    movie = swfdec_scriptable_from_jsval (cx, argv[0], SWFDEC_TYPE_MOVIE);
+  } else {
+    movie = NULL;
+  }
+  if (movie != NULL) {
+    if (!JS_SetPrivate (cx, obj, movie))
+      return JS_FALSE;
+    g_object_ref (movie);
   }
-  if (!JS_SetPrivate (cx, obj, movie))
-    return JS_TRUE;
-  g_object_ref (movie);
   *rval = OBJECT_TO_JSVAL (obj);
   return JS_TRUE;
 }
@@ -195,7 +200,7 @@ void
 swfdec_js_add_color (SwfdecPlayer *player)
 {
   JS_InitClass (player->jscx, player->jsobj, NULL,
-      &color_class, swfdec_js_color_new, 1, NULL, color_methods,
+      &color_class, swfdec_js_color_new, 0, NULL, color_methods,
       NULL, NULL);
 }
 
diff-tree 90bd9323aab5388329dfac37a165bd421818875c (from 97a972c232892d919de3045f69e6fc3fac4b8ad3)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 16:27:20 2007 +0100

    add print function for SetTarget

diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c
index 9921af8..7c24b10 100644
--- a/libswfdec/swfdec_script.c
+++ b/libswfdec/swfdec_script.c
@@ -1407,6 +1407,15 @@ swfdec_action_target_path (JSContext *cx
 /*** PRINT FUNCTIONS ***/
 
 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;
@@ -1733,7 +1742,7 @@ static const SwfdecActionSpec actions[25
   [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", NULL, 0, 0, { swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target } },
+  [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", NULL },
diff-tree 97a972c232892d919de3045f69e6fc3fac4b8ad3 (from 4b0e92902dcfd08e55f34d6d56aecb10efac9314)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 16:26:51 2007 +0100

    make eval (obj, "") return obj and not undefined

diff --git a/libswfdec/swfdec_js.c b/libswfdec/swfdec_js.c
index 829c1ad..4c14100 100644
--- a/libswfdec/swfdec_js.c
+++ b/libswfdec/swfdec_js.c
@@ -343,7 +343,7 @@ static gboolean
 swfdec_js_eval_internal (JSContext *cx, JSObject *obj, const char *str,
         jsval *val, gboolean set)
 {
-  jsval cur = JSVAL_NULL;
+  jsval cur;
   char *work = NULL;
 
   SWFDEC_LOG ("eval called with \"%s\" on %p", str, obj);
@@ -358,8 +358,8 @@ swfdec_js_eval_internal (JSContext *cx, 
     if (cx->fp == NULL)
       goto out;
     obj = cx->fp->thisp;
-    cur = OBJECT_TO_JSVAL (obj);
   }
+  cur = OBJECT_TO_JSVAL (obj);
   while (str != NULL && *str != '\0') {
     char *dot = strchr (str, '.');
     if (!JSVAL_IS_OBJECT (cur))
diff-tree 4b0e92902dcfd08e55f34d6d56aecb10efac9314 (from 29607ca556843663468425ffad907e69839a0d8f)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 16:26:04 2007 +0100

    add test for Color constructor

diff --git a/test/trace/Makefile.am b/test/trace/Makefile.am
index 443cba2..27f0cac 100644
--- a/test/trace/Makefile.am
+++ b/test/trace/Makefile.am
@@ -16,6 +16,8 @@ EXTRA_DIST = \
 	children.swf.trace \
 	color-getters.swf \
 	color-getters.swf.trace \
+	color-new.swf \
+	color-new.swf.trace \
 	color-setRGB.swf \
 	color-setRGB.swf.trace \
 	color-setTransform-alpha.swf \
diff --git a/test/trace/color-new.swf b/test/trace/color-new.swf
new file mode 100755
index 0000000..e9f9727
Binary files /dev/null and b/test/trace/color-new.swf differ
diff --git a/test/trace/color-new.swf.trace b/test/trace/color-new.swf.trace
new file mode 100755
index 0000000..eb220d7
--- /dev/null
+++ b/test/trace/color-new.swf.trace
@@ -0,0 +1,10 @@
+test behaviour of Color when created without a movie
+[object Object]
+undefined
+undefined
+[object Object]
+undefined
+undefined
+[object Object]
+undefined
+undefined
diff-tree 29607ca556843663468425ffad907e69839a0d8f (from 62447e55d8fb4b74154694683d47070cd49e9868)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 14:50:27 2007 +0100

    implement NextFrame, PreviousFrame, ToInteger, TargetPath, GotoLabel, GotoFrame2
    
    Those are the actions needed by South Park studio

diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c
index b172ec6..9921af8 100644
--- a/libswfdec/swfdec_script.c
+++ b/libswfdec/swfdec_script.c
@@ -35,6 +35,8 @@
 #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"
 
@@ -115,6 +117,25 @@ swfdec_action_push_string (JSContext *cx
   return JS_TRUE;
 }
 
+static double
+swfdec_action_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 isnan (d) ? 0 : d;
+  } else {
+    return 0;
+  }
+}
+
 /*** ALL THE ACTION IS HERE ***/
 
 static JSBool
@@ -140,6 +161,38 @@ swfdec_action_play (JSContext *cx, guint
 }
 
 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);
@@ -160,6 +213,76 @@ swfdec_action_goto_frame (JSContext *cx,
 }
 
 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 JSBool
+swfdec_action_goto_frame2 (JSContext *cx, guint action, const guint8 *data, guint len)
+{
+  SwfdecBits bits;
+  guint bias;
+  gboolean play;
+  jsval val;
+  int frame;
+  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--;
+  if (JSVAL_IS_STRING (val)) {
+    const char *name = swfdec_js_to_string (cx, val);
+    if (name == NULL)
+      return JS_FALSE;
+    if (strchr (name, ':')) {
+      SWFDEC_ERROR ("FIXME: handle targets");
+    }
+    frame = swfdec_sprite_get_frame (SWFDEC_SPRITE_MOVIE (movie)->sprite, name);
+    if (frame == -1)
+      return JS_TRUE;
+  } else {
+    /* FIXME: how do we treat undefined etc? */
+    frame = swfdec_action_to_number (cx, val);
+  }
+  frame += bias;
+  /* now set it */
+  movie = swfdec_action_get_target (cx);
+  if (movie) {
+    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 JSBool
 swfdec_action_wait_for_frame (JSContext *cx, guint action, const guint8 *data, guint len)
 {
   SwfdecMovie *movie = swfdec_action_get_target (cx);
@@ -551,25 +674,6 @@ out:
   return JS_TRUE;
 }
 
-static double
-swfdec_action_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 isnan (d) ? 0 : d;
-  } else {
-    return 0;
-  }
-}
-
 static JSBool
 swfdec_action_binary (JSContext *cx, guint action, const guint8 *data, guint len)
 {
@@ -1274,6 +1378,32 @@ swfdec_action_shift (JSContext *cx, guin
   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_action_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;
+}
+
 /*** PRINT FUNCTIONS ***/
 
 static char *
@@ -1431,6 +1561,26 @@ swfdec_action_print_constant_pool (guint
 }
 
 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;
@@ -1443,6 +1593,17 @@ swfdec_action_print_goto_frame (guint ac
 }
 
 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;
@@ -1474,8 +1635,8 @@ typedef struct {
 
 static const SwfdecActionSpec actions[256] = {
   /* version 3 */
-  [0x04] = { "NextFrame", NULL },
-  [0x05] = { "PreviousFrame", NULL },
+  [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 },
@@ -1494,10 +1655,10 @@ static const SwfdecActionSpec actions[25
   [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 },
+  [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] = { "SetTarget22", NULL, 1, 0, { swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2, swfdec_action_set_target2 } },
+  [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 } },
@@ -1532,7 +1693,7 @@ static const SwfdecActionSpec actions[25
   [0x42] = { "InitArray", NULL },
   [0x43] = { "InitObject", NULL, -1, 1, { NULL, NULL, swfdec_action_init_object, swfdec_action_init_object, swfdec_action_init_object } },
   [0x44] = { "Typeof", NULL },
-  [0x45] = { "TargetPath", NULL },
+  [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 }  },
@@ -1573,7 +1734,7 @@ static const SwfdecActionSpec actions[25
   /* 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", NULL, 0, 0, { swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target, swfdec_action_set_target } },
-  [0x8c] = { "GotoLabel", NULL },
+  [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", NULL },
   /* version 7 */
@@ -1590,7 +1751,7 @@ static const SwfdecActionSpec actions[25
   /* 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", 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 } }
 };
 
 char *
diff-tree 62447e55d8fb4b74154694683d47070cd49e9868 (from 75e9e227691415642b037d6be0ff7c2ccc90772c)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 14:48:56 2007 +0100

    add swfdec_movie_get_path
    
    The function returns the "path" as used in ActionScript

diff --git a/libswfdec/swfdec_js_movie.c b/libswfdec/swfdec_js_movie.c
index 70ddece..d33ba94 100644
--- a/libswfdec/swfdec_js_movie.c
+++ b/libswfdec/swfdec_js_movie.c
@@ -464,36 +464,19 @@ swfdec_js_getURL (JSContext *cx, JSObjec
   return JS_TRUE;
 }
 
-static GString *
-get_name (SwfdecMovie *movie)
-{
-  GString *s;
-
-  if (movie->parent) {
-    s = get_name (movie->parent);
-    g_string_append_c (s, '.');
-    g_string_append (s, movie->name);
-  } else {
-    /* the name can be changed */
-    s = g_string_new ("_level");
-    g_string_append_printf (s, "%u", movie->depth + 16384);
-  }
-  return s;
-}
-
 static JSBool
 swfdec_js_movie_to_string (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
-  GString *s;
+  char *s;
   JSString *string;
   SwfdecMovie *movie;
 
   movie = JS_GetPrivate (cx, obj);
   g_assert (movie);
 
-  s = get_name (movie);
-  string = JS_NewStringCopyZ (cx, s->str);
-  g_string_free (s, TRUE);
+  s = swfdec_movie_get_path (movie);
+  string = JS_NewStringCopyZ (cx, s);
+  g_free (s);
   if (string == NULL)
     return JS_FALSE;
   *rval = STRING_TO_JSVAL (string);
diff --git a/libswfdec/swfdec_movie.c b/libswfdec/swfdec_movie.c
index a851cd2..3ab9c88 100644
--- a/libswfdec/swfdec_movie.c
+++ b/libswfdec/swfdec_movie.c
@@ -787,6 +787,30 @@ swfdec_movie_goto (SwfdecMovie *movie, g
     klass->goto_frame (movie, frame);
 }
 
+char *
+swfdec_movie_get_path (SwfdecMovie *movie)
+{
+  GString *s;
+
+  g_return_val_if_fail (SWFDEC_IS_MOVIE (movie), NULL);
+  
+  s = g_string_new ("");
+  do {
+    if (movie->parent) {
+      g_string_prepend (s, movie->name);
+      g_string_prepend_c (s, '.');
+    } else {
+      char *ret = g_strdup_printf ("_level%u%s",
+	movie->depth + 16384, s->str);
+      g_string_free (s, TRUE);
+      return ret;
+    }
+    movie = movie->parent;
+  } while (TRUE);
+  g_assert_not_reached ();
+  return NULL;
+}
+
 int
 swfdec_movie_compare_depths (gconstpointer a, gconstpointer b)
 {
diff --git a/libswfdec/swfdec_movie.h b/libswfdec/swfdec_movie.h
index 61f495c..0351b54 100644
--- a/libswfdec/swfdec_movie.h
+++ b/libswfdec/swfdec_movie.h
@@ -177,6 +177,7 @@ void		swfdec_movie_send_mouse_change	(Sw
 SwfdecMovie *	swfdec_movie_get_movie_at	(SwfdecMovie *		movie,
 						 double			x,
 						 double			y);
+char *		swfdec_movie_get_path		(SwfdecMovie *		movie);
 void		swfdec_movie_render		(SwfdecMovie *		movie,
 						 cairo_t *		cr, 
 						 const SwfdecColorTransform *trans,
diff-tree 75e9e227691415642b037d6be0ff7c2ccc90772c (from ae5cb38d44ea9dabd46c38fd5231ef3afcfd86ac)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 10:32:33 2007 +0100

    add 2 tests for DefineFunction

diff --git a/test/trace/Makefile.am b/test/trace/Makefile.am
index 635e098..443cba2 100644
--- a/test/trace/Makefile.am
+++ b/test/trace/Makefile.am
@@ -42,6 +42,10 @@ EXTRA_DIST = \
 	currentframe.swf.trace \
 	double.swf \
 	double.swf.trace \
+	function1.swf \
+	function1.swf.trace \
+	function2.swf \
+	function2.swf.trace \
 	goto1.swf \
 	goto1.swf.trace \
 	goto2.swf \
diff --git a/test/trace/function1.swf b/test/trace/function1.swf
new file mode 100755
index 0000000..63f85d4
Binary files /dev/null and b/test/trace/function1.swf differ
diff --git a/test/trace/function1.swf.trace b/test/trace/function1.swf.trace
new file mode 100755
index 0000000..8403c7e
--- /dev/null
+++ b/test/trace/function1.swf.trace
@@ -0,0 +1,50 @@
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
+undefined
+0
+_level0
+1
+_level0
diff --git a/test/trace/function2.swf b/test/trace/function2.swf
new file mode 100755
index 0000000..23a6b46
Binary files /dev/null and b/test/trace/function2.swf differ
diff --git a/test/trace/function2.swf.trace b/test/trace/function2.swf.trace
new file mode 100755
index 0000000..59c46d4
--- /dev/null
+++ b/test/trace/function2.swf.trace
@@ -0,0 +1,2 @@
+[type Function]
+3
diff-tree ae5cb38d44ea9dabd46c38fd5231ef3afcfd86ac (from e2aa7731c273e897af818cadf20d5bb923d8c01e)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 10:30:41 2007 +0100

    implement CallFunction, BitRShift, BitLShift and BitURShift
    
    includes some fixes like implementing the function with name case

diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c
index c398e62..b172ec6 100644
--- a/libswfdec/swfdec_script.c
+++ b/libswfdec/swfdec_script.c
@@ -387,6 +387,33 @@ swfdec_action_call (JSContext *cx, guint
   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;
+  jsval fun;
+  
+  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 (n_args + 2 > (guint) (fp->sp - fp->spbase))
+    return JS_FALSE;
+  
+  obj = OBJ_THIS_OBJECT (cx, cx->fp->scopeChain);
+  if (!JS_GetProperty (cx, obj, s, &fun))
+    return JS_FALSE;
+  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)
 {
@@ -1207,7 +1234,9 @@ swfdec_action_define_function (JSContext
     }
     *cx->fp->sp++ = OBJECT_TO_JSVAL (fun->object);
   } else {
-    SWFDEC_ERROR ("FIXME: implement");
+    jsval val = OBJECT_TO_JSVAL (fun->object);
+    if (!JS_SetProperty (cx, OBJ_THIS_OBJECT (cx, cx->fp->scopeChain), function_name, &val))
+      return JS_FALSE;
   }
 
   /* update current context */
@@ -1215,6 +1244,36 @@ swfdec_action_define_function (JSContext
   return JS_TRUE;
 }
 
+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]);
+}
+
 /*** PRINT FUNCTIONS ***/
 
 static char *
@@ -1464,8 +1523,8 @@ static const SwfdecActionSpec actions[25
   /* version 5 */
   [0x3a] = { "Delete", NULL },
   [0x3b] = { "Delete2", NULL },
-  [0x3c] = { "DefineLocal", NULL },
-  [0x3d] = { "CallFunction", NULL },
+  [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 },
   [0x3f] = { "Modulo", NULL },
   [0x40] = { "NewObject", NULL, -1, 1, { NULL, NULL, swfdec_action_new_object, swfdec_action_new_object, swfdec_action_new_object } },
@@ -1495,9 +1554,9 @@ static const SwfdecActionSpec actions[25
   [0x60] = { "BitAnd", NULL },
   [0x61] = { "BitOr", NULL },
   [0x62] = { "BitXor", NULL },
-  [0x63] = { "BitLShift", NULL },
-  [0x64] = { "BitRShift", NULL },
-  [0x65] = { "BitURShift", NULL },
+  [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 } },
diff-tree e2aa7731c273e897af818cadf20d5bb923d8c01e (from ee7ecfa22195201642e114bfdb24d28aa690e00c)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 10:29:46 2007 +0100

    create a call object for calls to SWF code

diff --git a/libswfdec/js/jsinterp.c b/libswfdec/js/jsinterp.c
index 1d15966..ce8d014 100644
--- a/libswfdec/js/jsinterp.c
+++ b/libswfdec/js/jsinterp.c
@@ -963,7 +963,10 @@ have_fun:
         }
         ok = js_Interpret(cx, &v);
     } else if (swf) {
-        frame.scopeChain = funobj; /* FIXME */
+	if (!js_GetCallObject(cx, &frame, parent)) {
+	    ok = JS_FALSE;
+	    goto out;
+	}
         ok = swfdec_script_interpret(swf, cx, &v);
     } else {
         /* fun might be onerror trying to report a syntax error in itself. */
diff-tree ee7ecfa22195201642e114bfdb24d28aa690e00c (from 56d6b7a99657f96cc093d7136ff7eea4a6acfcfd)
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jan 31 10:28:46 2007 +0100

    return "[type Function]" from function.toString()

diff --git a/libswfdec/js/jsfun.c b/libswfdec/js/jsfun.c
index 84121de..a085673 100644
--- a/libswfdec/js/jsfun.c
+++ b/libswfdec/js/jsfun.c
@@ -1418,7 +1418,12 @@ js_fun_toString(JSContext *cx, JSObject 
 static JSBool
 fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
-    return js_fun_toString(cx, obj, 0, argc, argv, rval);
+    JSString *string = JS_InternString (cx, "[type Function]");
+
+    if (string == NULL)
+	return JS_FALSE;
+    *rval = STRING_TO_JSVAL (string);
+    return JS_TRUE;
 }
 
 #if JS_HAS_TOSOURCE
diff-tree 56d6b7a99657f96cc093d7136ff7eea4a6acfcfd (from bd449d3ebe7adc1a6fc90beafef2f192a6c23ac4)
Author: Benjamin Otte <otte at gnome.org>
Date:   Tue Jan 30 14:54:17 2007 +0100

    implement DefineFunction
    
    contains some changes to script handling that are required to make this
    work properly, like allowing scripts that don't end with a 0 action

diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c
index e2ddcec..c398e62 100644
--- a/libswfdec/swfdec_script.c
+++ b/libswfdec/swfdec_script.c
@@ -36,6 +36,7 @@
 #include "swfdec_player_internal.h"
 #include "swfdec_root_movie.h"
 #include "js/jsfun.h"
+#include "js/jsscope.h"
 
 /*** CONSTANT POOLS ***/
 
@@ -1134,9 +1135,129 @@ swfdec_action_init_object (JSContext *cx
   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;
+
+  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");
+    return JS_FALSE;
+  }
+  n_args = swfdec_bits_get_u16 (&bits);
+  if (*function_name == '\0') {
+    /* anonymous function */
+    fun = JS_NewFunction (cx, NULL, n_args, JSFUN_LAMBDA, NULL, NULL);
+  } else {
+    /* named function */
+    fun = JS_NewFunction (cx, NULL, n_args, 0, NULL, function_name);
+  }
+  if (fun == NULL)
+    return JS_FALSE;
+  for (i = 0; i < n_args; i++) {
+    JSAtom *atom;
+    const char *arg_name = swfdec_bits_get_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;
+    }
+  }
+  size = swfdec_bits_get_u16 (&bits);
+  /* check the script can be created */
+  script = cx->fp->swf;
+  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 */
+    SwfdecBuffer *buffer = swfdec_buffer_new_subbuffer (script->buffer, 
+	cx->fp->pc + 3 + len - script->buffer->data, size);
+    swfdec_bits_init (&bits, buffer);
+    script = swfdec_script_new_for_player (JS_GetContextPrivate (cx),
+	&bits, *function_name ? function_name : "<lambda>", 
+	((SwfdecScript *) cx->fp->swf)->version);
+    swfdec_buffer_unref (buffer);
+  }
+  if (script == NULL) {
+    SWFDEC_ERROR ("failed to create script");
+    return JS_FALSE;
+  }
+  fun->swf = script;
+  /* attach the function */
+  if (*function_name == '\0') {
+    if (action == 0) {
+      SWFDEC_ERROR ("not enough stack space available");
+      return JS_FALSE;
+    }
+    *cx->fp->sp++ = OBJECT_TO_JSVAL (fun->object);
+  } else {
+    SWFDEC_ERROR ("FIXME: implement");
+  }
+
+  /* update current context */
+  cx->fp->pc += 3 + len + size;
+  return JS_TRUE;
+}
+
 /*** PRINT FUNCTIONS ***/
 
 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;
+
+  string = g_string_new ("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, '(');
+ 
+  for (i = 0; i < n_args; i++) {
+    const char *arg_name = swfdec_bits_get_string (&bits);
+    if (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, ", ");
+    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_url (guint action, const guint8 *data, guint len)
 {
   SwfdecBits bits;
@@ -1406,7 +1527,7 @@ static const SwfdecActionSpec actions[25
   [0x99] = { "Jump", swfdec_action_print_jump, 0, 0, { NULL, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump, swfdec_action_jump } },
   [0x9a] = { "GetURL2", NULL },
   /* version 5 */
-  [0x9b] = { "DefineFunction", NULL },
+  [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 },
@@ -1442,7 +1563,7 @@ swfdec_script_foreach_internal (SwfdecBi
   gconstpointer bytecode;
 
   bytecode = bits->ptr;
-  while ((action = swfdec_bits_get_u8 (bits))) {
+  while (swfdec_bits_left (bits) && (action = swfdec_bits_get_u8 (bits))) {
     if (action & 0x80) {
       len = swfdec_bits_get_u16 (bits);
       data = bits->ptr;
@@ -1634,8 +1755,10 @@ swfdec_script_interpret (SwfdecScript *s
 
   while (TRUE) {
     /* check pc */
+    if (pc == endpc) /* needed for scripts created via DefineFunction */
+      break;
     if (pc < startpc || pc >= endpc) {
-      SWFDEC_ERROR ("pc %p not in valid range [%p, %p] anymore", pc, startpc, endpc);
+      SWFDEC_ERROR ("pc %p not in valid range [%p, %p) anymore", pc, startpc, endpc);
       goto internal_error;
     }
 
diff-tree bd449d3ebe7adc1a6fc90beafef2f192a6c23ac4 (from 2ae8e627b6ad7d5c5cba649db11cde9758e43815)
Author: Benjamin Otte <otte at gnome.org>
Date:   Tue Jan 30 14:52:20 2007 +0100

    fix SwfdecScript reference handling
    
    - intitialize fun->swf to NULL
    - unref fun->swf on finalization

diff --git a/libswfdec/js/jsfun.c b/libswfdec/js/jsfun.c
index 1ba58f8..84121de 100644
--- a/libswfdec/js/jsfun.c
+++ b/libswfdec/js/jsfun.c
@@ -1042,6 +1042,7 @@ fun_convert(JSContext *cx, JSObject *obj
     }
 }
 
+extern void swfdec_script_unref (void *script);
 static void
 fun_finalize(JSContext *cx, JSObject *obj)
 {
@@ -1058,6 +1059,8 @@ fun_finalize(JSContext *cx, JSObject *ob
         return;
     if (fun->script)
         js_DestroyScript(cx, fun->script);
+    if (fun->swf)
+	swfdec_script_unref (fun->swf);
     JS_free(cx, fun);
 }
 
@@ -1928,6 +1931,7 @@ js_NewFunction(JSContext *cx, JSObject *
     fun->object = NULL;
     fun->native = native;
     fun->script = NULL;
+    fun->swf = NULL;
     fun->nargs = nargs;
     fun->extra = 0;
     fun->nvars = 0;
diff-tree 2ae8e627b6ad7d5c5cba649db11cde9758e43815 (from 7b858d98456af8c801b9c346ef7abc8ebd4a0453)
Author: Benjamin Otte <otte at gnome.org>
Date:   Tue Jan 30 12:38:28 2007 +0100

    check !JSVAL_IS_NULL in all JSVAL_IS_OBJECT checks

diff --git a/libswfdec/swfdec_script.c b/libswfdec/swfdec_script.c
index 64f0746..e2ddcec 100644
--- a/libswfdec/swfdec_script.c
+++ b/libswfdec/swfdec_script.c
@@ -403,7 +403,7 @@ swfdec_action_call_method (JSContext *cx
   if (n_args + 3 > (guint) (fp->sp - fp->spbase))
     return JS_FALSE;
   
-  if (!JSVAL_IS_OBJECT (fp->sp[-2]))
+  if (!JSVAL_IS_OBJECT (fp->sp[-2]) || JSVAL_IS_NULL (fp->sp[-2]))
     goto fail;
   obj = JSVAL_TO_OBJECT (fp->sp[-2]);
   if (s[0] == '\0') {
@@ -689,7 +689,7 @@ swfdec_action_get_member (JSContext *cx,
   if (s == NULL)
     return JS_FALSE;
 
-  if (JSVAL_IS_OBJECT (cx->fp->sp[-2])) {
+  if (JSVAL_IS_OBJECT (cx->fp->sp[-2]) && !JSVAL_IS_NULL (cx->fp->sp[-2])) {
     if (!JS_GetProperty (cx, JSVAL_TO_OBJECT (cx->fp->sp[-2]), s, &cx->fp->sp[-2]))
       return JS_FALSE;
   } else {
@@ -708,7 +708,7 @@ swfdec_action_set_member (JSContext *cx,
   if (s == NULL)
     return JS_FALSE;
 
-  if (JSVAL_IS_OBJECT (cx->fp->sp[-3])) {
+  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;
   }
@@ -989,7 +989,7 @@ swfdec_action_set_target (JSContext *cx,
   }
   /* evaluate relative to this to not get trapped by previous SetTarget calls */
   target = swfdec_js_eval (cx, cx->fp->thisp, (const char *) data);
-  if (!JSVAL_IS_OBJECT (target)) {
+  if (!JSVAL_IS_OBJECT (target) || JSVAL_IS_NULL (target)) {
     SWFDEC_WARNING ("target is not an object");
     return JS_TRUE;
   }
@@ -1003,7 +1003,7 @@ swfdec_action_set_target2 (JSContext *cx
   
   val = cx->fp->sp[-1];
   cx->fp->sp--;
-  if (!JSVAL_IS_OBJECT (val)) {
+  if (!JSVAL_IS_OBJECT (val) || JSVAL_IS_NULL (val)) {
     SWFDEC_WARNING ("target is not an object");
     return JS_TRUE;
   }
@@ -1034,7 +1034,7 @@ swfdec_action_start_drag (JSContext *cx,
     fp->sp[-6] = fp->sp[-5];
     fp->sp[-5] = tmp;
   }
-  if (!JSVAL_IS_OBJECT (fp->sp[-1])) {
+  if (!JSVAL_IS_OBJECT (fp->sp[-1]) || JSVAL_IS_NULL (fp->sp[-1])) {
     fp->sp -= n_args + 2;
     return JS_TRUE;
   }
@@ -1084,7 +1084,7 @@ swfdec_action_new_object (JSContext *cx,
   }
   fp->sp[-1] = constructor;
 
-  if (!JSVAL_IS_OBJECT (constructor))
+  if (!JSVAL_IS_OBJECT (constructor) || JSVAL_IS_NULL (constructor))
     goto fail;
   object = JSVAL_TO_OBJECT (constructor);
   if (JS_GetClass (object) != &js_FunctionClass)
diff-tree 7b858d98456af8c801b9c346ef7abc8ebd4a0453 (from abfaad4612d9129e2cf0de1402f623e22d356831)
Author: Benjamin Otte <otte at gnome.org>
Date:   Tue Jan 30 12:34:20 2007 +0100

    catch a case where fun->script is accessed unconditionally

diff --git a/libswfdec/js/jsobj.c b/libswfdec/js/jsobj.c
index 2033c97..112bdc8 100644
--- a/libswfdec/js/jsobj.c
+++ b/libswfdec/js/jsobj.c
@@ -2356,6 +2356,8 @@ Detecting(JSContext *cx, jsbytecode *pc)
     JSAtom *atom;
 
     script = cx->fp->script;
+    if (script == NULL)
+      return JS_FALSE;
     for (endpc = script->code + script->length; pc < endpc; pc++) {
         /* General case: a branch or equality op follows the access. */
         op = (JSOp) *pc;
diff-tree abfaad4612d9129e2cf0de1402f623e22d356831 (from parents)
Merge: d8b4748701572b483c96bbb4a66a6f026cd1c1d7 a7b8850adba4086c321e69c8933f6248b3de0803
Author: Benjamin Otte <otte at gnome.org>
Date:   Tue Jan 30 09:59:37 2007 +0100

    Merge branch 'master' into interpreter

diff-tree a7b8850adba4086c321e69c8933f6248b3de0803 (from 133819a86a6435cce34751911118b74dd51f9bfc)
Author: Benjamin Otte <otte at gnome.org>
Date:   Tue Jan 30 09:56:54 2007 +0100

    add swfedit binary to gitignore

diff --git a/test/.gitignore b/test/.gitignore
index ef52ce4..9280a20 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -11,3 +11,4 @@ Makefile.in
 dump
 parse
 swfdec-extract
+swfedit
diff-tree 133819a86a6435cce34751911118b74dd51f9bfc (from parents)
Merge: d3add5d691f7832d56e3126003ce242822bfe11d 4e7806e7020535920cbb2009ed8b039fd64c046c
Author: Benjamin Otte <otte at gnome.org>
Date:   Mon Jan 29 22:34:49 2007 +0100

    Merge branch 'master' of ssh://company@git.freedesktop.org/git/swfdec

diff-tree d3add5d691f7832d56e3126003ce242822bfe11d (from f9f53b7a60b641e2ba5c846430f2fb48ac013eb0)
Author: Benjamin Otte <otte at gnome.org>
Date:   Mon Jan 29 22:33:27 2007 +0100

    rework audio configuration subsystem
    
    - make it easier to plug new backends
    - add a "none" backend when alsa isn't available

diff --git a/configure.ac b/configure.ac
index eb69535..e02adfd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -86,13 +86,40 @@ if test "$HAVE_GTK" = "no"; then
 fi
 AM_CONDITIONAL(WITH_GTK,[test "$HAVE_GTK" != "no"])
 
-PKG_CHECK_MODULES(ALSA, alsa >= 1.0, HAVE_ALSA=yes, HAVE_ALSA=no)
-AC_SUBST(ALSA_LIBS)
-AC_SUBST(ALSA_CFLAGS)
-if test "$HAVE_ALSA" = "no"; then
-  AC_MSG_WARN([cannot find alsa, player will be disabled])
+dnl
+dnl audio backend
+dnl
+AC_ARG_WITH(audio,
+            [AC_HELP_STRING([--with-audio=@<:@auto/alsa/none@:>@],
+                            [audio backend to use])],,
+	    [with_audio=auto])
+
+AUDIO_TYPE=
+if test "$with_audio" = "auto" -o "$with_audio" = "alsa"; then
+  PKG_CHECK_MODULES(ALSA, alsa >= 1.0, AUDIO_TYPE=alsa)
+  if test "$AUDIO_TYPE" = "alsa"; then
+    with_audio=alsa
+  else
+    AC_MSG_WARN([no alsa audio support])
+  fi
+  AUDIO_CFLAGS=$ALSA_CFLAGS
+  AUDIO_LIBS=$ALSA_LIBS
+fi
+
+if test "$with_audio" = "auto" -o "$with_audio" = "none"; then
+  AUDIO_CFLAGS=
+  AUDIO_LIBS=
+  AUDIO_TYPE=none
 fi
-AM_CONDITIONAL(WITH_ALSA,[test "$HAVE_ALSA" != "no"])
+
+if test "x$AUDIO_TYPE" = "x"; then
+  AC_MSG_ERROR([desired audio support could not be used])
+else
+  AC_MSG_NOTICE([audio backend: $AUDIO_TYPE])
+fi
+AC_SUBST(AUDIO_LIBS)
+AC_SUBST(AUDIO_CFLAGS)
+AC_SUBST(AUDIO_TYPE)
 
 PKG_CHECK_MODULES(LIBOIL, liboil-0.3 >= 0.3.1.1, HAVE_LIBOIL=yes, HAVE_LIBOIL=no)
 AC_SUBST(LIBOIL_LIBS)
diff --git a/player/.gitignore b/player/.gitignore
index 3e3d43c..f2dfbf1 100644
--- a/player/.gitignore
+++ b/player/.gitignore
@@ -9,6 +9,7 @@ Makefile.in
 *.o
 *.lo
 *.loT
+swfdec_playback.c
 
 libswfdecui.la
 swfdebug
diff --git a/player/Makefile.am b/player/Makefile.am
index 0a29cc5..c56ee91 100644
--- a/player/Makefile.am
+++ b/player/Makefile.am
@@ -1,5 +1,13 @@
 if WITH_GTK
-if WITH_ALSA
+
+# this workaround is needed or autotools don't generate the .deps/*.Plo files correctly
+swfdec_playback.c: swfdec_playback_$(AUDIO_TYPE).c Makefile
+	cmp -s $(srcdir)/swfdec_playback_$(AUDIO_TYPE).c swfdec_playback.c \
+	|| cp $(srcdir)/swfdec_playback_$(AUDIO_TYPE).c swfdec_playback.c
+
+BUILT_SOURCES = swfdec_playback.c
+CLEANFILES = swfdec_playback.c
+
 noinst_LTLIBRARIES = libswfdecui.la
 noinst_PROGRAMS = swfplay swfdebug
 
@@ -31,15 +39,17 @@ noinst_HEADERS = \
 	swfdec_slow_loader.h \
 	swfdec_widget.h 
 
-libswfdecui_la_CFLAGS = $(GLOBAL_CFLAGS) $(GTK_CFLAGS) $(SWF_CFLAGS) $(ALSA_CFLAGS)
-libswfdecui_la_LDFLAGS = $(GTK_LIBS) $(SWF_LIBS) $(ALSA_LIBS)
-swfplay_CFLAGS = $(GLOBAL_CFLAGS) $(GTK_CFLAGS) $(SWF_CFLAGS) $(ALSA_CFLAGS)
-swfplay_LDFLAGS = $(GTK_LIBS) $(SWF_LIBS) $(ALSA_LIBS)
+libswfdecui_la_CFLAGS = $(GLOBAL_CFLAGS) $(GTK_CFLAGS) $(SWF_CFLAGS) $(AUDIO_CFLAGS)
+libswfdecui_la_LDFLAGS = $(GTK_LIBS) $(SWF_LIBS) $(AUDIO_LIBS)
+swfplay_CFLAGS = $(GLOBAL_CFLAGS) $(GTK_CFLAGS) $(SWF_CFLAGS)
+swfplay_LDFLAGS = $(GTK_LIBS) $(SWF_LIBS)
 swfplay_LDADD = libswfdecui.la
-swfdebug_CFLAGS = $(GLOBAL_CFLAGS) $(GTK_CFLAGS) $(SWF_CFLAGS) $(ALSA_CFLAGS) -DXP_UNIX -I$(top_builddir)/libswfdec/js
-swfdebug_LDFLAGS = $(GTK_LIBS) $(SWF_LIBS) $(ALSA_LIBS)
+swfdebug_CFLAGS = $(GLOBAL_CFLAGS) $(GTK_CFLAGS) $(SWF_CFLAGS) -DXP_UNIX -I$(top_builddir)/libswfdec/js
+swfdebug_LDFLAGS = $(GTK_LIBS) $(SWF_LIBS)
 swfdebug_LDADD = libswfdecui.la $(top_builddir)/libswfdec/js/libjs.la
 endif
-endif
 
-EXTRA_DIST = swfdebug.c swfplay.c swfdec_widget.c swfdec_widget.h swfdec_playback.c swfdec_playback.h
+EXTRA_DIST = \
+	swfdec_playback_alsa.c \
+	swfdec_playback_none.c
+
diff --git a/player/swfdec_playback.c b/player/swfdec_playback.c
deleted file mode 100644
index ea47099..0000000
--- a/player/swfdec_playback.c
+++ /dev/null
@@ -1,355 +0,0 @@
-/* Swfdec
- * Copyright (C) 2006 Benjamin Otte <otte at 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 <alsa/asoundlib.h>
-#include "swfdec_source.h"
-
-/* Why ALSA sucks for beginners:
- * - snd_pcm_delay is not sample-exact, but period-exact most of the time.
- *   Yay for getting told the time every 512 samples when a human notices
- *   a delay of 100 samples (oooops)
- * - lots of functions are simply not implemented. So the super-smart idea
- *   of using snd_pcm_rewind to avoid XRUNS and still get low latency has
- *   some issues when dmix just returns -EIO all of the time. That wouldn't
- *   be so bad if there was actually a way to query if it's supported.
- * - But to make up for all this, you have 10 hardware parameters, 10 
- *   software parameters and 10 configuration parameters. All of this is
- *   naturally supported on 10 driver APIs depending on kernel. So if your
- *   sound card seems to do weird stuff, that is not my fault.
- * Welcome to Linux sound in the 21st century.
- */
-
-/*** DEFINITIONS ***/
-
-typedef struct {
-  SwfdecPlayer *	player;
-  GList *		streams;	/* all Stream objects */
-  GMainContext *	context;	/* context we work in */
-} Sound;
-
-typedef struct {
-  Sound *		sound;		/* reference to sound object */
-  SwfdecAudio *		audio;		/* the audio we play back */
-  snd_pcm_t *		pcm;		/* the pcm we play back to */
-  GSource **		sources;	/* sources for writing data */
-  guint			n_sources;	/* number of sources */
-  guint			offset;		/* offset into sound */
-} Stream;
-
-#define ALSA_TRY(func,msg) G_STMT_START{ \
-  int err = func; \
-  if (err < 0) \
-    g_printerr (msg ": %s\n", snd_strerror (err)); \
-}G_STMT_END
-
-#define ALSA_ERROR(func,msg,retval) G_STMT_START { \
-  int err = func; \
-  if (err < 0) { \
-    g_printerr (msg ": %s\n", snd_strerror (err)); \
-    return retval; \
-  } \
-}G_STMT_END
-
-/*** STREAMS ***/
-
-static snd_pcm_uframes_t
-write_player (Stream *stream, const snd_pcm_channel_area_t *dst, 
-    snd_pcm_uframes_t offset, snd_pcm_uframes_t avail)
-{
-  /* FIXME: do a long path if this doesn't hold */
-  g_assert (dst[1].first - dst[0].first == 16);
-  g_assert (dst[0].addr == dst[1].addr);
-  g_assert (dst[0].step == dst[1].step);
-  g_assert (dst[0].step == 32);
-
-  memset (dst[0].addr + offset * dst[0].step / 8, 0, avail * 4);
-  swfdec_audio_render (stream->audio, dst[0].addr + offset * dst[0].step / 8, 
-      stream->offset, avail);
-  //g_print ("rendering %u %u\n", stream->offset, (guint) avail);
-  return avail;
-}
-
-static gboolean
-try_write (Stream *stream)
-{
-  snd_pcm_sframes_t avail_result;
-  snd_pcm_uframes_t offset, avail;
-  const snd_pcm_channel_area_t *dst;
-
-  while (TRUE) {
-    avail_result = snd_pcm_avail_update (stream->pcm);
-    ALSA_ERROR (avail_result, "snd_pcm_avail_update failed", FALSE);
-    if (avail_result == 0)
-      return TRUE;
-    avail = avail_result;
-    ALSA_ERROR (snd_pcm_mmap_begin (stream->pcm, &dst, &offset, &avail),
-	"snd_pcm_mmap_begin failed", FALSE);
-    //g_print ("  avail = %u\n", (guint) avail);
-
-    avail = write_player (stream, dst, offset, avail);
-    if (snd_pcm_mmap_commit (stream->pcm, offset, avail) < 0) {
-      g_printerr ("snd_pcm_mmap_commit failed\n");
-      return FALSE;
-    }
-    stream->offset += avail;
-    //g_print ("offset: %u (+%u)\n", stream->offset, (guint) avail);
-  }
-  return TRUE;
-}
-
-static void
-swfdec_stream_remove_handlers (Stream *stream)
-{
-  unsigned int i;
-
-  for (i = 0; i < stream->n_sources; i++) {
-    if (stream->sources[i]) {
-      g_source_destroy (stream->sources[i]);
-      g_source_unref (stream->sources[i]);
-      stream->sources[i] = NULL;
-    }
-  }
-}
-
-static void swfdec_stream_start (Stream *stream);
-static gboolean
-handle_stream (GIOChannel *source, GIOCondition cond, gpointer data)
-{
-  Stream *stream = data;
-  snd_pcm_state_t state;
-
-  state = snd_pcm_state (stream->pcm);
-  if (state != SND_PCM_STATE_RUNNING) {
-    swfdec_stream_start (stream);
-  } else {
-    try_write (stream);
-  }
-  return TRUE;
-}
-
-static void
-swfdec_stream_install_handlers (Stream *stream)
-{
-  if (stream->n_sources > 0) {
-    struct pollfd polls[stream->n_sources];
-    unsigned int i, count;
-    if (stream->n_sources > 1)
-      g_printerr ("attention: more than one fd!\n");
-    count = snd_pcm_poll_descriptors (stream->pcm, polls, stream->n_sources);
-    for (i = 0; i < count; i++) {
-      if (stream->sources[i] != NULL)
-	continue;
-      GIOChannel *channel = g_io_channel_unix_new (polls[i].fd);
-      stream->sources[i] = g_io_create_watch (channel, polls[i].events);
-      g_source_set_priority (stream->sources[i], G_PRIORITY_HIGH);
-      g_source_set_callback (stream->sources[i], (GSourceFunc) handle_stream, stream, NULL);
-      g_io_channel_unref (channel);
-      g_source_attach (stream->sources[i], stream->sound->context);
-    }
-  }
-}
-
-static void
-swfdec_stream_start (Stream *stream)
-{
-  snd_pcm_state_t state = snd_pcm_state (stream->pcm);
-  switch (state) {
-    case SND_PCM_STATE_XRUN:
-      ALSA_ERROR (snd_pcm_prepare (stream->pcm), "no prepare",);
-      //g_print ("XRUN!\n");
-      /* fall through */
-    case SND_PCM_STATE_SUSPENDED:
-    case SND_PCM_STATE_PREPARED:
-      stream->offset = 0;
-      //g_print ("offset: %u (delay: %ld)\n", sound->offset, delay);
-      if (try_write (stream)) {
-	ALSA_ERROR (snd_pcm_start (stream->pcm), "error starting",);
-	swfdec_stream_install_handlers (stream);
-      }
-      break;
-    default:
-      break;
-  }
-}
-
-static void
-swfdec_stream_open (Sound *sound, SwfdecAudio *audio)
-{
-  Stream *stream;
-  snd_pcm_t *ret;
-  snd_pcm_hw_params_t *hw_params;
-  unsigned int rate;
-  snd_pcm_uframes_t uframes;
-
-  /* "default" uses dmix, and dmix ticks way slow, so this thingy here stutters */
-  ALSA_ERROR (snd_pcm_open (&ret, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK),
-      "Failed to open sound device", );
-
-  snd_pcm_hw_params_alloca (&hw_params);
-  if (snd_pcm_hw_params_any (ret, hw_params) < 0) {
-    g_printerr ("No sound format available\n");
-    return;
-  }
-  if (snd_pcm_hw_params_set_access (ret, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
-    g_printerr ("Failed setting access\n");
-    goto fail;
-  }
-  if (snd_pcm_hw_params_set_format (ret, hw_params, SND_PCM_FORMAT_S16) < 0) {
-    g_printerr ("Failed setting format\n");
-    goto fail;
-  }
-  if (snd_pcm_hw_params_set_channels (ret, hw_params, 2) < 0) {
-    g_printerr ("Failed setting channels\n");
-    goto fail;
-  }
-  rate = 44100;
-  if (snd_pcm_hw_params_set_rate_near (ret, hw_params, &rate, 0) < 0) {
-    g_printerr ("Failed setting rate\n");
-    goto fail;
-  }
-  uframes = 16384;
-  if (snd_pcm_hw_params_set_buffer_size_near (ret, hw_params, &uframes) < 0) {
-    g_printerr ("Failed setting buffer size\n");
-    goto fail;
-  }
-  if (snd_pcm_hw_params (ret, hw_params) < 0) {
-    g_printerr ("Could not set hardware parameters\n");
-    goto fail;
-  }
-#if 0
-  {
-    snd_output_t *log;
-    snd_output_stdio_attach (&log, stderr, 0);
-    snd_pcm_hw_params_dump (hw_params, log);
-  }
-#endif
-  stream = g_new0 (Stream, 1);
-  stream->sound = sound;
-  stream->audio = g_object_ref (audio);
-  stream->pcm = ret;
-  stream->n_sources = snd_pcm_poll_descriptors_count (ret);
-  if (stream->n_sources > 0)
-    stream->sources = g_new0 (GSource *, stream->n_sources);
-  sound->streams = g_list_prepend (sound->streams, stream);
-  swfdec_stream_start (stream);
-  return;
-
-fail:
-  snd_pcm_close (ret);
-}
-
-static void
-swfdec_stream_close (Stream *stream)
-{
-  ALSA_TRY (snd_pcm_close (stream->pcm), "failed closing");
-  swfdec_stream_remove_handlers (stream);
-  g_free (stream->sources);
-  stream->sound->streams = g_list_remove (stream->sound->streams, stream);
-  g_object_unref (stream->audio);
-  g_free (stream);
-}
-
-/*** SOUND ***/
-
-static void
-advance_before (SwfdecPlayer *player, guint msecs, guint audio_samples, gpointer data)
-{
-  Sound *sound = data;
-  GList *walk;
-
-  for (walk = sound->streams; walk; walk = walk->next) {
-    Stream *stream = walk->data;
-    if (audio_samples >= stream->offset) {
-      stream->offset = 0;
-    } else {
-      stream->offset -= audio_samples;
-    }
-  }
-}
-
-static void
-audio_added (SwfdecPlayer *player, SwfdecAudio *audio, Sound *sound)
-{
-  swfdec_stream_open (sound, audio);
-}
-
-static void
-audio_removed (SwfdecPlayer *player, SwfdecAudio *audio, Sound *sound)
-{
-  GList *walk;
-
-  for (walk = sound->streams; walk; walk = walk->next) {
-    Stream *stream = walk->data;
-    if (stream->audio == audio) {
-      swfdec_stream_close (stream);
-      return;
-    }
-  }
-  g_assert_not_reached ();
-}
-
-gpointer
-swfdec_playback_open (SwfdecPlayer *player, GMainContext *context)
-{
-  Sound *sound;
-  const GList *walk;
-
-  g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
-  g_return_val_if_fail (context != NULL, NULL);
-
-  sound = g_new0 (Sound, 1);
-  sound->player = g_object_ref (player);
-  g_signal_connect (player, "advance", G_CALLBACK (advance_before), sound);
-  g_signal_connect (player, "audio-added", G_CALLBACK (audio_added), sound);
-  g_signal_connect (player, "audio-removed", G_CALLBACK (audio_removed), sound);
-  for (walk = swfdec_player_get_audio (player); walk; walk = walk->next) {
-    swfdec_stream_open (sound, walk->data);
-  }
-  g_main_context_ref (context);
-  sound->context = context;
-  return sound;
-}
-
-void
-swfdec_playback_close (gpointer data)
-{
-  Sound *sound = data;
-#define REMOVE_HANDLER_FULL(obj,func,data,count) G_STMT_START {\
-  if (g_signal_handlers_disconnect_by_func ((obj), \
-	G_CALLBACK (func), (data)) != (count)) { \
-    g_assert_not_reached (); \
-  } \
-} G_STMT_END
-#define REMOVE_HANDLER(obj,func,data) REMOVE_HANDLER_FULL (obj, func, data, 1)
-
-  while (sound->streams)
-    swfdec_stream_close (sound->streams->data);
-  REMOVE_HANDLER (sound->player, advance_before, sound);
-  REMOVE_HANDLER (sound->player, audio_added, sound);
-  REMOVE_HANDLER (sound->player, audio_removed, sound);
-  g_object_unref (sound->player);
-  g_main_context_unref (sound->context);
-  g_free (sound);
-}
-
-
diff --git a/player/swfdec_playback_alsa.c b/player/swfdec_playback_alsa.c
new file mode 100644
index 0000000..ea47099
--- /dev/null
+++ b/player/swfdec_playback_alsa.c
@@ -0,0 +1,355 @@
+/* Swfdec
+ * Copyright (C) 2006 Benjamin Otte <otte at 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 <alsa/asoundlib.h>
+#include "swfdec_source.h"
+
+/* Why ALSA sucks for beginners:
+ * - snd_pcm_delay is not sample-exact, but period-exact most of the time.
+ *   Yay for getting told the time every 512 samples when a human notices
+ *   a delay of 100 samples (oooops)
+ * - lots of functions are simply not implemented. So the super-smart idea
+ *   of using snd_pcm_rewind to avoid XRUNS and still get low latency has
+ *   some issues when dmix just returns -EIO all of the time. That wouldn't
+ *   be so bad if there was actually a way to query if it's supported.
+ * - But to make up for all this, you have 10 hardware parameters, 10 
+ *   software parameters and 10 configuration parameters. All of this is
+ *   naturally supported on 10 driver APIs depending on kernel. So if your
+ *   sound card seems to do weird stuff, that is not my fault.
+ * Welcome to Linux sound in the 21st century.
+ */
+
+/*** DEFINITIONS ***/
+
+typedef struct {
+  SwfdecPlayer *	player;
+  GList *		streams;	/* all Stream objects */
+  GMainContext *	context;	/* context we work in */
+} Sound;
+
+typedef struct {
+  Sound *		sound;		/* reference to sound object */
+  SwfdecAudio *		audio;		/* the audio we play back */
+  snd_pcm_t *		pcm;		/* the pcm we play back to */
+  GSource **		sources;	/* sources for writing data */
+  guint			n_sources;	/* number of sources */
+  guint			offset;		/* offset into sound */
+} Stream;
+
+#define ALSA_TRY(func,msg) G_STMT_START{ \
+  int err = func; \
+  if (err < 0) \
+    g_printerr (msg ": %s\n", snd_strerror (err)); \
+}G_STMT_END
+
+#define ALSA_ERROR(func,msg,retval) G_STMT_START { \
+  int err = func; \
+  if (err < 0) { \
+    g_printerr (msg ": %s\n", snd_strerror (err)); \
+    return retval; \
+  } \
+}G_STMT_END
+
+/*** STREAMS ***/
+
+static snd_pcm_uframes_t
+write_player (Stream *stream, const snd_pcm_channel_area_t *dst, 
+    snd_pcm_uframes_t offset, snd_pcm_uframes_t avail)
+{
+  /* FIXME: do a long path if this doesn't hold */
+  g_assert (dst[1].first - dst[0].first == 16);
+  g_assert (dst[0].addr == dst[1].addr);
+  g_assert (dst[0].step == dst[1].step);
+  g_assert (dst[0].step == 32);
+
+  memset (dst[0].addr + offset * dst[0].step / 8, 0, avail * 4);
+  swfdec_audio_render (stream->audio, dst[0].addr + offset * dst[0].step / 8, 
+      stream->offset, avail);
+  //g_print ("rendering %u %u\n", stream->offset, (guint) avail);
+  return avail;
+}
+
+static gboolean
+try_write (Stream *stream)
+{
+  snd_pcm_sframes_t avail_result;
+  snd_pcm_uframes_t offset, avail;
+  const snd_pcm_channel_area_t *dst;
+
+  while (TRUE) {
+    avail_result = snd_pcm_avail_update (stream->pcm);
+    ALSA_ERROR (avail_result, "snd_pcm_avail_update failed", FALSE);
+    if (avail_result == 0)
+      return TRUE;
+    avail = avail_result;
+    ALSA_ERROR (snd_pcm_mmap_begin (stream->pcm, &dst, &offset, &avail),
+	"snd_pcm_mmap_begin failed", FALSE);
+    //g_print ("  avail = %u\n", (guint) avail);
+
+    avail = write_player (stream, dst, offset, avail);
+    if (snd_pcm_mmap_commit (stream->pcm, offset, avail) < 0) {
+      g_printerr ("snd_pcm_mmap_commit failed\n");
+      return FALSE;
+    }
+    stream->offset += avail;
+    //g_print ("offset: %u (+%u)\n", stream->offset, (guint) avail);
+  }
+  return TRUE;
+}
+
+static void
+swfdec_stream_remove_handlers (Stream *stream)
+{
+  unsigned int i;
+
+  for (i = 0; i < stream->n_sources; i++) {
+    if (stream->sources[i]) {
+      g_source_destroy (stream->sources[i]);
+      g_source_unref (stream->sources[i]);
+      stream->sources[i] = NULL;
+    }
+  }
+}
+
+static void swfdec_stream_start (Stream *stream);
+static gboolean
+handle_stream (GIOChannel *source, GIOCondition cond, gpointer data)
+{
+  Stream *stream = data;
+  snd_pcm_state_t state;
+
+  state = snd_pcm_state (stream->pcm);
+  if (state != SND_PCM_STATE_RUNNING) {
+    swfdec_stream_start (stream);
+  } else {
+    try_write (stream);
+  }
+  return TRUE;
+}
+
+static void
+swfdec_stream_install_handlers (Stream *stream)
+{
+  if (stream->n_sources > 0) {
+    struct pollfd polls[stream->n_sources];
+    unsigned int i, count;
+    if (stream->n_sources > 1)
+      g_printerr ("attention: more than one fd!\n");
+    count = snd_pcm_poll_descriptors (stream->pcm, polls, stream->n_sources);
+    for (i = 0; i < count; i++) {
+      if (stream->sources[i] != NULL)
+	continue;
+      GIOChannel *channel = g_io_channel_unix_new (polls[i].fd);
+      stream->sources[i] = g_io_create_watch (channel, polls[i].events);
+      g_source_set_priority (stream->sources[i], G_PRIORITY_HIGH);
+      g_source_set_callback (stream->sources[i], (GSourceFunc) handle_stream, stream, NULL);
+      g_io_channel_unref (channel);
+      g_source_attach (stream->sources[i], stream->sound->context);
+    }
+  }
+}
+
+static void
+swfdec_stream_start (Stream *stream)
+{
+  snd_pcm_state_t state = snd_pcm_state (stream->pcm);
+  switch (state) {
+    case SND_PCM_STATE_XRUN:
+      ALSA_ERROR (snd_pcm_prepare (stream->pcm), "no prepare",);
+      //g_print ("XRUN!\n");
+      /* fall through */
+    case SND_PCM_STATE_SUSPENDED:
+    case SND_PCM_STATE_PREPARED:
+      stream->offset = 0;
+      //g_print ("offset: %u (delay: %ld)\n", sound->offset, delay);
+      if (try_write (stream)) {
+	ALSA_ERROR (snd_pcm_start (stream->pcm), "error starting",);
+	swfdec_stream_install_handlers (stream);
+      }
+      break;
+    default:
+      break;
+  }
+}
+
+static void
+swfdec_stream_open (Sound *sound, SwfdecAudio *audio)
+{
+  Stream *stream;
+  snd_pcm_t *ret;
+  snd_pcm_hw_params_t *hw_params;
+  unsigned int rate;
+  snd_pcm_uframes_t uframes;
+
+  /* "default" uses dmix, and dmix ticks way slow, so this thingy here stutters */
+  ALSA_ERROR (snd_pcm_open (&ret, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK),
+      "Failed to open sound device", );
+
+  snd_pcm_hw_params_alloca (&hw_params);
+  if (snd_pcm_hw_params_any (ret, hw_params) < 0) {
+    g_printerr ("No sound format available\n");
+    return;
+  }
+  if (snd_pcm_hw_params_set_access (ret, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
+    g_printerr ("Failed setting access\n");
+    goto fail;
+  }
+  if (snd_pcm_hw_params_set_format (ret, hw_params, SND_PCM_FORMAT_S16) < 0) {
+    g_printerr ("Failed setting format\n");
+    goto fail;
+  }
+  if (snd_pcm_hw_params_set_channels (ret, hw_params, 2) < 0) {
+    g_printerr ("Failed setting channels\n");
+    goto fail;
+  }
+  rate = 44100;
+  if (snd_pcm_hw_params_set_rate_near (ret, hw_params, &rate, 0) < 0) {
+    g_printerr ("Failed setting rate\n");
+    goto fail;
+  }
+  uframes = 16384;
+  if (snd_pcm_hw_params_set_buffer_size_near (ret, hw_params, &uframes) < 0) {
+    g_printerr ("Failed setting buffer size\n");
+    goto fail;
+  }
+  if (snd_pcm_hw_params (ret, hw_params) < 0) {
+    g_printerr ("Could not set hardware parameters\n");
+    goto fail;
+  }
+#if 0
+  {
+    snd_output_t *log;
+    snd_output_stdio_attach (&log, stderr, 0);
+    snd_pcm_hw_params_dump (hw_params, log);
+  }
+#endif
+  stream = g_new0 (Stream, 1);
+  stream->sound = sound;
+  stream->audio = g_object_ref (audio);
+  stream->pcm = ret;
+  stream->n_sources = snd_pcm_poll_descriptors_count (ret);
+  if (stream->n_sources > 0)
+    stream->sources = g_new0 (GSource *, stream->n_sources);
+  sound->streams = g_list_prepend (sound->streams, stream);
+  swfdec_stream_start (stream);
+  return;
+
+fail:
+  snd_pcm_close (ret);
+}
+
+static void
+swfdec_stream_close (Stream *stream)
+{
+  ALSA_TRY (snd_pcm_close (stream->pcm), "failed closing");
+  swfdec_stream_remove_handlers (stream);
+  g_free (stream->sources);
+  stream->sound->streams = g_list_remove (stream->sound->streams, stream);
+  g_object_unref (stream->audio);
+  g_free (stream);
+}
+
+/*** SOUND ***/
+
+static void
+advance_before (SwfdecPlayer *player, guint msecs, guint audio_samples, gpointer data)
+{
+  Sound *sound = data;
+  GList *walk;
+
+  for (walk = sound->streams; walk; walk = walk->next) {
+    Stream *stream = walk->data;
+    if (audio_samples >= stream->offset) {
+      stream->offset = 0;
+    } else {
+      stream->offset -= audio_samples;
+    }
+  }
+}
+
+static void
+audio_added (SwfdecPlayer *player, SwfdecAudio *audio, Sound *sound)
+{
+  swfdec_stream_open (sound, audio);
+}
+
+static void
+audio_removed (SwfdecPlayer *player, SwfdecAudio *audio, Sound *sound)
+{
+  GList *walk;
+
+  for (walk = sound->streams; walk; walk = walk->next) {
+    Stream *stream = walk->data;
+    if (stream->audio == audio) {
+      swfdec_stream_close (stream);
+      return;
+    }
+  }
+  g_assert_not_reached ();
+}
+
+gpointer
+swfdec_playback_open (SwfdecPlayer *player, GMainContext *context)
+{
+  Sound *sound;
+  const GList *walk;
+
+  g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
+  g_return_val_if_fail (context != NULL, NULL);
+
+  sound = g_new0 (Sound, 1);
+  sound->player = g_object_ref (player);
+  g_signal_connect (player, "advance", G_CALLBACK (advance_before), sound);
+  g_signal_connect (player, "audio-added", G_CALLBACK (audio_added), sound);
+  g_signal_connect (player, "audio-removed", G_CALLBACK (audio_removed), sound);
+  for (walk = swfdec_player_get_audio (player); walk; walk = walk->next) {
+    swfdec_stream_open (sound, walk->data);
+  }
+  g_main_context_ref (context);
+  sound->context = context;
+  return sound;
+}
+
+void
+swfdec_playback_close (gpointer data)
+{
+  Sound *sound = data;
+#define REMOVE_HANDLER_FULL(obj,func,data,count) G_STMT_START {\
+  if (g_signal_handlers_disconnect_by_func ((obj), \
+	G_CALLBACK (func), (data)) != (count)) { \
+    g_assert_not_reached (); \
+  } \
+} G_STMT_END
+#define REMOVE_HANDLER(obj,func,data) REMOVE_HANDLER_FULL (obj, func, data, 1)
+
+  while (sound->streams)
+    swfdec_stream_close (sound->streams->data);
+  REMOVE_HANDLER (sound->player, advance_before, sound);
+  REMOVE_HANDLER (sound->player, audio_added, sound);
+  REMOVE_HANDLER (sound->player, audio_removed, sound);
+  g_object_unref (sound->player);
+  g_main_context_unref (sound->context);
+  g_free (sound);
+}
+
+
diff --git a/player/swfdec_playback_none.c b/player/swfdec_playback_none.c
new file mode 100644
index 0000000..6464a4a
--- /dev/null
+++ b/player/swfdec_playback_none.c
@@ -0,0 +1,38 @@
+/* Swfdec
+ * Copyright (C) 2007 Benjamin Otte <otte at 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_playback.h"
+
+/* STUBS ONLY - audio is disabled */
+
+gpointer
+swfdec_playback_open (SwfdecPlayer *player, GMainContext *context)
+{
+  return GINT_TO_POINTER (-1);
+}
+
+void
+swfdec_playback_close (gpointer sound)
+{
+  g_assert (sound == GINT_TO_POINTER (-1));
+}
diff-tree f9f53b7a60b641e2ba5c846430f2fb48ac013eb0 (from 2cb8680a8577ff5d018b50e7da55c233e8eaa4af)
Author: Benjamin Otte <otte at gnome.org>
Date:   Mon Jan 29 22:31:13 2007 +0100

    enable gtk-doc in autogen.sh
    
    developers are supposed to ensure that docs build and make dist requires it

diff --git a/autogen.sh b/autogen.sh
index 13824b4..fee343b 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,3 +1,3 @@
 #!/bin/sh
 autoreconf -i -f &&
-./configure --enable-maintainer-mode --disable-static $@
+./configure --enable-maintainer-mode --disable-static --enable-gtk-doc $@
diff-tree 4e7806e7020535920cbb2009ed8b039fd64c046c (from 2cb8680a8577ff5d018b50e7da55c233e8eaa4af)
Author: Laszlo (Laca) Peter <laca at sun.com>
Date:   Fri Jan 26 14:57:50 2007 -0800

    Bug #9660: Fix build on Solaris.

diff --git a/libswfdec/swfdec_debug.h b/libswfdec/swfdec_debug.h
index 66e116a..2a59774 100644
--- a/libswfdec/swfdec_debug.h
+++ b/libswfdec/swfdec_debug.h
@@ -46,7 +46,7 @@ enum {
 #define SWFDEC_DEBUG_LEVEL(level,...) (void) 0
 #else
 #define SWFDEC_DEBUG_LEVEL(level,...) \
-  swfdec_debug_log ((level), __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
+  swfdec_debug_log ((level), __FILE__, G_GNUC_FUNCTION, __LINE__, __VA_ARGS__)
 #endif
 
 void swfdec_debug_log (unsigned int level, const char *file, const char *function,


More information about the Swfdec mailing list