Example #1
0
gboolean
swfdec_amf_parse_one (SwfdecAsContext *context, SwfdecBits *bits, 
    SwfdecAmfType expected_type, SwfdecAsValue *rval)
{
  SwfdecAmfParseFunc func;
  guint type;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), 0);
  g_return_val_if_fail (context->global != NULL, 0);
  g_return_val_if_fail (bits != NULL, FALSE);
  g_return_val_if_fail (rval != NULL, FALSE);
  g_return_val_if_fail (expected_type < SWFDEC_AMF_N_TYPES, FALSE);

  type = swfdec_bits_get_u8 (bits);
  if (type != expected_type) {
    SWFDEC_ERROR ("parse object should be type %u, but is %u", 
	expected_type, type);
    return FALSE;
  }
  if (type >= SWFDEC_AMF_N_TYPES ||
      (func = parse_funcs[type]) == NULL) {
    SWFDEC_ERROR ("no parse func for AMF type %u", type);
    return FALSE;
  }
  return func (context, bits, rval);
}
Example #2
0
void
swfdec_as_function_init_context (SwfdecAsContext *context)
{
  SwfdecAsObject *function, *proto;
  SwfdecAsValue val;

  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (context));

  function = SWFDEC_AS_OBJECT (swfdec_as_object_add_function (context->global,
      SWFDEC_AS_STR_Function, NULL));
  swfdec_as_object_set_variable_flags (context->global, SWFDEC_AS_STR_Function, SWFDEC_AS_VARIABLE_VERSION_6_UP);
  context->Function = function;
  SWFDEC_AS_VALUE_SET_OBJECT (&val, function);
  swfdec_as_object_set_variable_and_flags (function, SWFDEC_AS_STR_constructor,
      &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
  proto = swfdec_as_object_new_empty (context);
  context->Function_prototype = proto;
  SWFDEC_AS_VALUE_SET_OBJECT (&val, proto);
  swfdec_as_object_set_variable_and_flags (function, SWFDEC_AS_STR_prototype,
      &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
  swfdec_as_object_set_variable_and_flags (function, SWFDEC_AS_STR___proto__,
      &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT |
      SWFDEC_AS_VARIABLE_VERSION_6_UP);
  SWFDEC_AS_VALUE_SET_OBJECT (&val, function);
  swfdec_as_object_set_variable_and_flags (proto, SWFDEC_AS_STR_constructor,
      &val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
}
SwfdecColorTransformAs *
swfdec_color_transform_as_new_from_transform (SwfdecAsContext *context,
    const SwfdecColorTransform *transform)
{
  SwfdecColorTransformAs *transform_as;
  SwfdecAsObject *object;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
  g_return_val_if_fail (transform != NULL, NULL);

  transform_as = g_object_new (SWFDEC_TYPE_COLOR_TRANSFORM_AS, "context", context, NULL);
  /* do it this way so the constructor isn't called */
  object = swfdec_as_object_new (context, NULL);
  swfdec_as_object_set_relay (object, SWFDEC_AS_RELAY (transform_as));
  swfdec_as_object_set_constructor_by_name (object, 
      SWFDEC_AS_STR_flash, SWFDEC_AS_STR_geom, SWFDEC_AS_STR_ColorTransform, NULL);

  transform_as->ra = transform->ra / 256.0;
  transform_as->ga = transform->ga / 256.0;
  transform_as->ba = transform->ba / 256.0;
  transform_as->aa = transform->aa / 256.0;
  transform_as->rb = transform->rb;
  transform_as->gb = transform->gb;
  transform_as->bb = transform->bb;
  transform_as->ab = transform->ab;

  return transform_as;
}
/**
 * swfdec_as_native_function_new:
 * @context: a #SwfdecAsContext
 * @name: name of the function
 * @native: function to call when executed
 * @prototype: The object to be used as "prototype" property for the created 
 *             function or %NULL for none.
 *
 * Creates a new native function, that will execute @native when called. You 
 * might want to use swfdec_as_object_add_function() instead of this function.
 *
 * Returns: a new #SwfdecAsFunction
 **/
SwfdecAsFunction *
swfdec_as_native_function_new (SwfdecAsContext *context, const char *name,
    SwfdecAsNative native, SwfdecAsObject *prototype)
{
  SwfdecAsNativeFunction *fun;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
  g_return_val_if_fail (native != NULL, NULL);
  g_return_val_if_fail (prototype == NULL || SWFDEC_IS_AS_OBJECT (prototype), NULL);

  fun = g_object_new (SWFDEC_TYPE_AS_NATIVE_FUNCTION, "context", context, NULL);
  fun->native = native;
  fun->name = g_strdup (name);
  /* need to set prototype before setting the constructor or Function.constructor 
   * being CONSTANT disallows setting it. */
  if (prototype) {
    SwfdecAsValue val;
    SWFDEC_AS_VALUE_SET_OBJECT (&val, prototype);
    swfdec_as_object_set_variable_and_flags (SWFDEC_AS_OBJECT (fun), SWFDEC_AS_STR_prototype, 
	&val, SWFDEC_AS_VARIABLE_HIDDEN | SWFDEC_AS_VARIABLE_PERMANENT);
  }
  swfdec_as_function_set_constructor (SWFDEC_AS_FUNCTION (fun));

  return SWFDEC_AS_FUNCTION (fun);
}
static void
swfdec_print_job_init_properties (SwfdecAsContext *cx)
{
  SwfdecAsValue val;
  SwfdecAsObject *xml, *proto;

  // FIXME: We should only initialize if the prototype Object has not been
  // initialized by any object's constructor with native properties
  // (TextField, TextFormat, XML, XMLNode at least)

  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (cx));

  swfdec_as_object_get_variable (cx->global, SWFDEC_AS_STR_PrintJob, &val);
  if (!SWFDEC_AS_VALUE_IS_OBJECT (val))
    return;
  xml = SWFDEC_AS_VALUE_GET_OBJECT (val);

  swfdec_as_object_get_variable (xml, SWFDEC_AS_STR_prototype, &val);
  if (!SWFDEC_AS_VALUE_IS_OBJECT (val))
    return;
  proto = SWFDEC_AS_VALUE_GET_OBJECT (val);

  swfdec_as_object_add_native_variable (proto, SWFDEC_AS_STR_orientation,
      swfdec_print_job_get_orientation, NULL);
  swfdec_as_object_add_native_variable (proto, SWFDEC_AS_STR_pageHeight,
      swfdec_print_job_get_pageHeight, NULL);
  swfdec_as_object_add_native_variable (proto, SWFDEC_AS_STR_pageWidth,
      swfdec_print_job_get_pageWidth, NULL);
  swfdec_as_object_add_native_variable (proto, SWFDEC_AS_STR_paperHeight,
      swfdec_print_job_get_paperHeight, NULL);
  swfdec_as_object_add_native_variable (proto, SWFDEC_AS_STR_paperWidth,
      swfdec_print_job_get_paperWidth, NULL);
}
Example #6
0
// inner function for swfdec_as_array_sort_compare
static int
swfdec_as_array_sort_compare_values (SwfdecAsContext *cx,
    const SwfdecAsValue *a, const SwfdecAsValue *b, SortOption options,
    SwfdecAsFunction *custom_function)
{
  int retval;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (cx), 0);
  g_return_val_if_fail (SWFDEC_IS_AS_VALUE (a), 0);
  g_return_val_if_fail (SWFDEC_IS_AS_VALUE (b), 0);
  g_return_val_if_fail (custom_function == NULL ||
      SWFDEC_IS_AS_FUNCTION (custom_function), 0);

  if (custom_function != NULL)
  {
    SwfdecAsValue argv[2] = { *a, *b };
    SwfdecAsValue ret;
    SwfdecAsContext *context = swfdec_gc_object_get_context (custom_function);
    swfdec_as_function_call (custom_function, NULL, 2, argv, &ret);
    retval = swfdec_as_value_to_integer (context, &ret);
  }
  else if (options & SORT_OPTION_NUMERIC &&
      (SWFDEC_AS_VALUE_IS_NUMBER (a) ||
       SWFDEC_AS_VALUE_IS_NUMBER (b)) &&
      !SWFDEC_AS_VALUE_IS_UNDEFINED (a) &&
      !SWFDEC_AS_VALUE_IS_UNDEFINED (b))
  {
    if (!SWFDEC_AS_VALUE_IS_NUMBER (a)) {
      retval = 1;
    } else if (!SWFDEC_AS_VALUE_IS_NUMBER (b)) {
      retval = -1;
    } else {
      double an = swfdec_as_value_to_number (cx, a);
      double bn = swfdec_as_value_to_number (cx, b);
      retval = (an < bn ? -1 : (an > bn ? 1 : 0));
    }
  }
  else
  {
    // can't pass swfdec_as_value_to_string calls directly to compare
    // functions, since the order of these is important
    const char *a_str = swfdec_as_value_to_string (cx, a);
    const char *b_str = swfdec_as_value_to_string (cx, b);

    if (options & SORT_OPTION_CASEINSENSITIVE) {
      retval = g_strcasecmp (a_str, b_str);
    } else {
      retval = strcmp (a_str, b_str);
    }
  }

  if (options & SORT_OPTION_DESCENDING)
    retval = -retval;

  return retval;
}
Example #7
0
static int
swfdec_as_array_sort_compare (gconstpointer a_ptr, gconstpointer b_ptr,
    gpointer user_data)
{
  const SwfdecAsValue *a = &((SortEntry *)a_ptr)->value;
  const SwfdecAsValue *b = &((SortEntry *)b_ptr)->value;
  SortCompareData *data = user_data;
  int retval;

  g_return_val_if_fail (SWFDEC_IS_AS_VALUE (a), 0);
  g_return_val_if_fail (SWFDEC_IS_AS_VALUE (b), 0);
  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (data->context), 0);
  g_return_val_if_fail (data->options != NULL, 0);
  g_return_val_if_fail (data->custom_function == NULL ||
      SWFDEC_IS_AS_FUNCTION (data->custom_function), 0);
  g_return_val_if_fail (data->fields == NULL || data->fields[0] != NULL, 0);

  if (data->fields == NULL) {
    retval = swfdec_as_array_sort_compare_values (data->context, a, b,
	data->options[0], data->custom_function);
  } else {
    SwfdecAsValue a_comp, b_comp;
    SwfdecAsObject *object;
    int i;

    i = 0;
    do {
      object = swfdec_as_value_to_object (data->context, a);
      if (object) {
	swfdec_as_object_get_variable (object, data->fields[i], &a_comp);
      } else {
	SWFDEC_AS_VALUE_SET_UNDEFINED (&a_comp);
      }

      object = swfdec_as_value_to_object (data->context, b);
      if (object) {
	swfdec_as_object_get_variable (object, data->fields[i], &b_comp);
      } else {
	SWFDEC_AS_VALUE_SET_UNDEFINED (&b_comp);
      }

      retval =
	swfdec_as_array_sort_compare_values (data->context, &a_comp, &b_comp,
	    data->options[i], data->custom_function);
    } while (retval == 0 && data->fields[++i] != NULL);
  }

  if (retval == 0)
    data->equal_found = TRUE;

  return retval;
}
Example #8
0
/**
 * swfdec_as_array_new:
 * @context: a #SwfdecAsContext
 *
 * Creates a new #SwfdecAsArray. 
 *
 * Returns: the new array
 **/
SwfdecAsObject *
swfdec_as_array_new (SwfdecAsContext *context)
{
  SwfdecAsObject *ret;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);

  ret = g_object_new (SWFDEC_TYPE_AS_ARRAY, "context", context, NULL);
  swfdec_as_object_set_constructor_by_name (ret, SWFDEC_AS_STR_Array, NULL);

  swfdec_as_array_set_length_object (ret, 0);

  return ret;
}
/**
 * swfdec_as_native_function_check:
 * @cx: a #SwfdecAsContext
 * @object: this object passed to the native function
 * @type: expected type of @object or 0 for any
 * @result: pointer to variable taking cast result of @object
 * @argc: count of arguments passed to the function
 * @argv: arguments passed to the function
 * @args: argument conversion string
 * @...: pointers to variables taking converted arguments
 *
 * This function is a convenience function to validate and convert arguments to 
 * a native function while avoiding common pitfalls. You typically want to call
 * it at the beginning of every native function you write. Or you can use the 
 * SWFDEC_AS_CHECK() macro instead which calls this function.
 * The @cx, @object, @argc and @argv paramters should be passed verbatim from 
 * the function call to your native function. If @type is not 0, @object is then
 * checked to be of that type and cast to @result. After that the @args string 
 * is used to convert the arguments. Every character in @args describes the 
 * conversion of one argument. For that argument, you have to pass a pointer 
 * that takes the value. For the conversion, the default conversion functions 
 * like swfdec_as_value_to_string() are used. If not enough arguments are 
 * available, the function stops converting and returns %NULL. The following 
 * conversion characters are allowed:<itemizedlist>
 * <listitem><para>"b": convert to boolean. Requires a %gboolean pointer
 *                 </para></listitem>
 * <listitem><para>"i": convert to integer. Requires an %integer pointer
 *                 </para></listitem>
 * <listitem><para>"m": convert to an existing movieclip. This is only valid 
 *                 inside Swfdec. Requires a %SwfdecMovie pointer.
 *                 </para></listitem>
 * <listitem><para>"M": convert to a movieclip or %NULL. This is only valid 
 *                 inside Swfdec. If the movie has already been destroyed, 
 *                 the pointer will be set to %NULL</para></listitem>
 * <listitem><para>"n": convert to number. Requires a %double pointer
 *                 </para></listitem>
 * <listitem><para>"o": convert to object. Requires a #SwfdecAsObject pointer.
 *                 If the conversion fails, this function immediately returns
 *                 %FALSE.</para></listitem>
 * <listitem><para>"O": convert to object or %NULL. Requires a #SwfdecAsObject
 *                 pointer.</para></listitem>
 * <listitem><para>"s": convert to garbage-collected string. Requires a const 
 *                 %char pointer</para></listitem>
 * <listitem><para>"v": copy the value. The given argument must be a pointer 
 *                 to a #SwfdecAsValue</para></listitem>
 * <listitem><para>"|": optional arguments follow. Optional arguments will be
 *		   initialized to the empty value for their type. This 
 *		   conversion character is only allowed once in the conversion 
 *		   string.</para></listitem>
 * </itemizedlist>
 *
 * Returns: %TRUE if the conversion succeeded, %FALSE otherwise
 **/
gboolean
swfdec_as_native_function_check (SwfdecAsContext *cx, SwfdecAsObject *object, 
    GType type, gpointer *result, guint argc, SwfdecAsValue *argv, 
    const char *args, ...)
{
  gboolean ret;
  va_list varargs;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (cx), FALSE);
  g_return_val_if_fail (type == 0 || result != NULL, FALSE);

  va_start (varargs, args);
  ret = swfdec_as_native_function_checkv (cx, object, type, result, argc, argv, args, varargs);
  va_end (varargs);
  return ret;
}
/**
 * swfdec_as_native_function_new:
 * @context: a #SwfdecAsContext
 * @name: name of the function
 * @native: function to call when executed
 *
 * Creates a new native function, that will execute @native when called. You 
 * might want to use swfdec_as_object_add_function() instead of this function.
 *
 * Returns: a new #SwfdecAsFunction
 **/
SwfdecAsFunction *
swfdec_as_native_function_new (SwfdecAsContext *context, const char *name,
    SwfdecAsNative native)
{
  SwfdecAsFunction *fun;
  SwfdecAsObject *object;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
  g_return_val_if_fail (native != NULL, NULL);

  fun = swfdec_as_native_function_new_bare (context, name, native);
  object = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (fun));

  swfdec_as_object_set_constructor_by_name (object, SWFDEC_AS_STR_Function, NULL);
  swfdec_as_object_set_variable_flags (object, SWFDEC_AS_STR___proto__, SWFDEC_AS_VARIABLE_VERSION_6_UP);

  return fun;
}
SwfdecAsFunction *
swfdec_as_native_function_new_bare (SwfdecAsContext *context, const char *name,
    SwfdecAsNative native)
{
  SwfdecAsNativeFunction *fun;
  SwfdecAsObject *object;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
  g_return_val_if_fail (native != NULL, NULL);

  fun = g_object_new (SWFDEC_TYPE_AS_NATIVE_FUNCTION, "context", context, NULL);
  fun->native = native;
  fun->name = g_strdup (name);

  object = swfdec_as_object_new_empty (context);
  swfdec_as_object_set_relay (object, SWFDEC_AS_RELAY (fun));

  return SWFDEC_AS_FUNCTION (fun);
}
Example #12
0
guint
swfdec_amf_parse (SwfdecAsContext *context, SwfdecBits *bits, guint n_items, ...)
{
  va_list args;
  guint i;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), 0);
  g_return_val_if_fail (context->global != NULL, 0);
  g_return_val_if_fail (bits != NULL, 0);

  va_start (args, n_items);
  for (i = 0; i < n_items; i++) {
    SwfdecAmfType type = va_arg (args, SwfdecAmfType);
    SwfdecAsValue *val = va_arg (args, SwfdecAsValue *);
    if (!swfdec_amf_parse_one (context, bits, type, val))
      break;
  }
  va_end (args);
  return i;
}
Example #13
0
SwfdecBitmapData *
swfdec_bitmap_data_new (SwfdecAsContext *context, gboolean transparent, guint width, guint height)
{
  SwfdecBitmapData *bitmap;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (context), NULL);
  g_return_val_if_fail (width > 0, NULL);
  g_return_val_if_fail (height > 0, NULL);

  if (!swfdec_as_context_try_use_mem (context, width * height * 4))
    return NULL;

  bitmap = g_object_new (SWFDEC_TYPE_BITMAP_DATA, "context", context, NULL);
  bitmap->surface = cairo_image_surface_create (
      transparent ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height);

  swfdec_as_object_set_constructor_by_name (SWFDEC_AS_OBJECT (bitmap),
      SWFDEC_AS_STR_flash, SWFDEC_AS_STR_display, SWFDEC_AS_STR_BitmapData, NULL);

  return bitmap;
}
/**
 * swfdec_as_native_function_checkv:
 * @cx: a #SwfdecAsContext
 * @object: this object passed to the native function
 * @type: expected type of @object
 * @result: pointer to variable taking cast result of @object
 * @argc: count of arguments passed to the function
 * @argv: arguments passed to the function
 * @args: argument conversion string
 * @varargs: pointers to variables taking converted arguments
 *
 * This is the valist version of swfdec_as_native_function_check(). See that
 * function for details.
 *
 * Returns: %TRUE if the conversion succeeded, %FALSE otherwise
 **/
gboolean
swfdec_as_native_function_checkv (SwfdecAsContext *cx, SwfdecAsObject *object, 
    GType type, gpointer *result, guint argc, SwfdecAsValue *argv, 
    const char *args, va_list varargs)
{
  guint i;
  gboolean optional = FALSE;

  g_return_val_if_fail (SWFDEC_IS_AS_CONTEXT (cx), FALSE);
  g_return_val_if_fail (type == 0 || result != NULL, FALSE);

  /* check that we got a valid type */
  if (type) {
    if (G_TYPE_CHECK_INSTANCE_TYPE (object, type)) {
      *result = object;
    } else if (object && G_TYPE_CHECK_INSTANCE_TYPE (object->relay, type)) {
      *result = object->relay;
    } else {
	return FALSE;
    }
  }
  for (i = 0; *args && i < argc; i++, args++) {
    switch (*args) {
      case 'v':
	{
	  SwfdecAsValue *val = va_arg (varargs, SwfdecAsValue *);
	  *val = argv[i];
	}
	break;
      case 'b':
	{
	  gboolean *b = va_arg (varargs, gboolean *);
	  *b = swfdec_as_value_to_boolean (cx, argv[i]);
	}
	break;
      case 'i':
	{
	  int *j = va_arg (varargs, int *);
	  *j = swfdec_as_value_to_integer (cx, argv[i]);
	}
	break;
      case 'm':
      case 'M':
	{
	  SwfdecMovie *m;
	  SwfdecMovie **arg = va_arg (varargs, SwfdecMovie **);
	  if (SWFDEC_AS_VALUE_IS_MOVIE (argv[i])) {
	    m = SWFDEC_AS_VALUE_GET_MOVIE (argv[i]);
	  } else {
	    m = NULL;
	  }
	  if (m == NULL && *args != 'M')
	    return FALSE;
	  *arg = m;
	}
	break;
      case 'n':
	{
	  double *d = va_arg (varargs, double *);
	  *d = swfdec_as_value_to_number (cx, argv[i]);
	}
	break;
      case 's':
	{
	  const char **s = va_arg (varargs, const char **);
	  *s = swfdec_as_value_to_string (cx, argv[i]);
	}
	break;
      case 'o':
      case 'O':
	{
	  SwfdecAsObject **o = va_arg (varargs, SwfdecAsObject **);
	  *o = swfdec_as_value_to_object (cx, argv[i]);
	  if (*o == NULL && *args != 'O')
	    return FALSE;
	}
	break;
      case '|':
	g_return_val_if_fail (optional == FALSE, FALSE);
	optional = TRUE;
	i--;
	break;
      default:
	g_warning ("'%c' is not a valid type conversion", *args);
	return FALSE;
    }
  }
  if (*args && !optional && *args != '|')
    return FALSE;
  return TRUE;
}
Example #15
0
static void
swfdec_as_array_do_sort (SwfdecAsContext *cx, SwfdecAsObject *object,
    const SortOption *options, SwfdecAsFunction *custom_function,
    const char **fields, SwfdecAsValue *ret)
{
  SortEntry *array;
  SortCollectData collect_data;
  SortCompareData compare_data;
  gint32 i, length;
  const char *var;
  SwfdecAsObject *target;
  SwfdecAsValue val;
  SortOption options_;
  gboolean descending;

  g_return_if_fail (SWFDEC_IS_AS_CONTEXT (cx));
  g_return_if_fail (SWFDEC_IS_AS_OBJECT (object));
  g_return_if_fail (options != NULL);
  g_return_if_fail (custom_function == NULL ||
      SWFDEC_IS_AS_FUNCTION (custom_function));
  g_return_if_fail (fields == NULL || fields[0] != NULL);

  length = swfdec_as_array_length (object);
  if (length == 0) {
    // special case for empty array, because g_try_new0 would return NULL
    SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
    return;
  }

  if (!swfdec_as_context_try_use_mem (cx, sizeof (SortEntry) * length)) {
    SWFDEC_WARNING ("Array not sorted, too big (%i elements)", length);
    SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
    return;
  }

  // FIXME: this should be different, but context's memory management is not
  // done properly yet
  array = g_try_new0 (SortEntry, length);
  if (!array) {
    SWFDEC_WARNING ("Array not sorted, too big (%i elements)", length);
    SWFDEC_AS_VALUE_SET_OBJECT (ret, object);
    goto done;
  }

  for (i = 0; i < length; i++) {
    array[i].index_ = i;
  }

  // collect values and indexes to the array
  collect_data.length = length;
  collect_data.array = array;

  swfdec_as_object_foreach (object, swfdec_as_array_foreach_sort_collect,
	&collect_data);

  // sort the array
  compare_data.context = cx;
  compare_data.fields = fields;
  compare_data.options = options;
  // if no fields, then we'll do descending here after the sort
  if (fields == NULL && options[0] & SORT_OPTION_DESCENDING) {
    descending = TRUE;
    options_ = options[0] & ~SORT_OPTION_DESCENDING;
    compare_data.options = &options_;
  } else {
    descending = FALSE;
  }
  compare_data.custom_function = custom_function;
  compare_data.equal_found = FALSE;

  swfdec_as_array_sort_array (array, length, swfdec_as_array_sort_compare,
      &compare_data);

  // check unique sort
  if (options[0] & SORT_OPTION_UNIQUESORT) {
    if (fields == NULL && custom_function != NULL) {
      // if we used custom_function for comparision, we shall now go trough the
      // sorted array and compare them using the default array sort comparision
      // and use that to decide if it's unique (no it doesn't make too much
      // sense)
      for (i = 0; i < length - 1; i++) {
	SwfdecAsValue *a = &array[i].value;
	SwfdecAsValue *b = &array[i + 1].value;
	if (swfdec_as_array_sort_compare_values (cx, a, b, 0, NULL) == 0)
	  break;
      }
      if (i < length - 1) {
	SWFDEC_AS_VALUE_SET_INT (ret, 0);
	goto done;
      }
    } else if (compare_data.equal_found) {
      SWFDEC_AS_VALUE_SET_INT (ret, 0);
      goto done;
    }
  }

  if (options[0] & SORT_OPTION_RETURNINDEXEDARRAY) {
    target = swfdec_as_array_new (cx);
    if (!target)
      goto done;
  } else {
    target = object;
  }

  for (i = 0; i < length; i++) {
    SortEntry *entry = &array[i];

    // set only the values that have new indexes
    if (!(options[0] & SORT_OPTION_RETURNINDEXEDARRAY) &&
	entry->index_ == (descending ? length - i - 1 : i))
      continue;

    var = swfdec_as_integer_to_string (cx, (descending ? length - i - 1 : i));
    if (options[0] & SORT_OPTION_RETURNINDEXEDARRAY) {
      SWFDEC_AS_VALUE_SET_INT (&val, entry->index_);
      swfdec_as_object_set_variable (target, var, &val);
    } else {
      swfdec_as_object_set_variable (target, var, &entry->value);
    }
  }

  SWFDEC_AS_VALUE_SET_OBJECT (ret, target);

done:
  g_free (array);
  swfdec_as_context_unuse_mem (cx, sizeof (SortEntry) * length);
}