Beispiel #1
0
void qspCallSaveGame(QSP_CHAR *file)
{
	/* Здесь позволяем пользователю выбрать файл */
	/* для сохранения состояния игры и сохраняем */
	/* в нем текущее состояние */
	QSPCallState state;
	AS3_Val args;
	char *strUTF8;
	if (qspCallBacks[QSP_CALL_SAVEGAMESTATUS].IsSet)
	{
		qspSaveCallState(&state, QSP_FALSE, QSP_TRUE);
		if (file)
		{
			strUTF8 = qspW2C(file);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_SAVEGAMESTATUS].FuncVal, qspCallBacks[QSP_CALL_SAVEGAMESTATUS].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #2
0
QSP_CHAR *qspCallInputBox(QSP_CHAR *text)
{
	/* Здесь вводим текст */
	QSPCallState state;
	QSP_CHAR *buffer;
	AS3_Val args;
	char *strUTF8;
	char *resText;
	int maxLen = 511;
	if (qspCallBacks[QSP_CALL_INPUTBOX].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		if (text)
		{
			strUTF8 = qspW2C(text);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_INPUTBOX].FuncVal, qspCallBacks[QSP_CALL_INPUTBOX].ThisVal, args);
		AS3_Release(args);
		flyield();
		resText = AS3_StringValue(result);
		AS3_Release(result);
		buffer = qspC2W(resText);
		free(resText);
		qspRestoreCallState(&state);
	}
	else
		buffer = qspGetNewText(QSP_FMT(""), 0);
	return buffer;
}
Beispiel #3
0
QSP_BOOL qspCallIsPlayingFile(QSP_CHAR *file)
{
	/* Здесь проверяем, проигрывается ли файл */
	QSPCallState state;
	QSP_BOOL isPlaying;
	AS3_Val args;
	char *strUTF8;
	if (qspCallBacks[QSP_CALL_ISPLAYINGFILE].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		if (file)
		{
			strUTF8 = qspW2C(file);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_ISPLAYINGFILE].FuncVal, qspCallBacks[QSP_CALL_ISPLAYINGFILE].ThisVal, args);
		AS3_Release(args);
		flyield();
		isPlaying = (QSP_BOOL)AS3_IntValue(result);
		AS3_Release(result);
		qspRestoreCallState(&state);
		return isPlaying;
	}
	return QSP_FALSE;
}
Beispiel #4
0
/*
* Create an ActionScript value from the lua stack starting
* at index start and ending at index end.  If collapse_array == 1,
* an empty return will be transformed into AS3_Undefined() and a
* return of length 1 will just return the specific value.
* Otherwise an array is returned.
*/
AS3_Val create_as3_value_from_lua_stack(
    lua_State * L,
    int start,
    int end,
    BOOL collapse_array
  )
{
  /* WARNING: Panic alert! Use L*_FN checkers here! */

  LCALL(L, stack);
  AS3_Val ret;

  SPAM(("create_as3_value_from_lua_stack(): begin"));

  if (collapse_array == TRUE && start > end)
  {
    ret = AS3_Null();
  }
  else if (collapse_array == TRUE && start == end)
  {
    ret = get_as3_value_from_lua_stack(L, start);
  }
  else
  {
    int i;

    ret = AS3_Array("");
    for (i = start; i <= end; ++i)
    {
      AS3_Val value;
      AS3_Val r;

      /*SPAM(("create_as3_value_from_lua_stack() + 1 begin"));*/

      value = get_as3_value_from_lua_stack(L, i);
      r = AS3_CallTS("push", ret, "AS3ValType", value);
      SAFE_RELEASE(r); /* Ignoring result */

      SAFE_RELEASE(value);

      /*SPAM(("create_as3_value_from_lua_stack() + 1 end"));*/
    }
  }

#ifdef DO_SPAM
  SPAM(("create_as3_value_from_lua_stack(): end"));
  AS3_Trace(
      AS3_Call(
          getQualifiedClassName_method, NULL, AS3_Array("AS3ValType", ret)
        )
    );
#endif /* DO_SPAM */

  LCHECK_FN(L, stack, 0, fatal_error);

  return ret;
}
Beispiel #5
0
/*
* Given an ActionScript object, push it onto the Lua stack as a Lua native
* type if a primitive class (String, Number, Boolean, int, null).
* If object is not convertible to native Lua value, do not push anything
* (and return 0).
*
* WARNING: It important that this function does not touch
*          non-primitive values (like Arrays). If this will be changed,
*          optional primitive autoconversion logic will break.
*/
int push_as3_to_lua_stack_if_convertible(lua_State * L, AS3_Val val)
{
  LCALL(L, stack);

#ifdef DO_SPAM
  SPAM(("push_as3_to_lua_stack_if_convertible(): begin: value, type"));
  AS3_Trace(val);
  AS3_Trace(
      AS3_Call(
          getQualifiedClassName_method, NULL, AS3_Array("AS3ValType", val)
        )
    );
#endif /* DO_SPAM */

  if (AS3_InstanceOf(val, Number_class))
  {
    lua_pushnumber(L, AS3_NumberValue(val));
  }
  else if (AS3_InstanceOf(val, int_class))
  {
    lua_pushinteger(L, AS3_IntValue(val));
  }
  else if (AS3_InstanceOf(val, String_class))
  {
    size_t length = 0;
    AS3_Malloced_Str str = get_string_bytes(val, &length);
    lua_pushlstring(L, str, length);
    free(str);
  }
  else if (AS3_InstanceOf(val, Boolean_class))
  {
    lua_pushboolean(L, AS3_IntValue(val));
  }
  else if (val == AS3_Undefined())
  {
    lua_pushnil(L);
  }
  else if (is_null(val))
  {
    lua_pushnil(L);
  }
  else
  {
    SPAM(("push_as3_to_lua_stack_if_convertible(): not convertible"));
    LRETURN(L, stack, 0);
  }

  SPAM(("push_as3_to_lua_stack_if_convertible(): end"));

  LRETURN(L, stack, 1);
}
Beispiel #6
0
void qspCallSleep(int msecs)
{
	/* Здесь ожидаем заданное количество миллисекунд */
	QSPCallState state;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_SLEEP].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		args = AS3_Array("IntType", msecs);
		AS3_Call(qspCallBacks[QSP_CALL_SLEEP].FuncVal, qspCallBacks[QSP_CALL_SLEEP].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #7
0
void qspCallDeleteMenu()
{
	/* Здесь удаляем текущее меню */
	QSPCallState state;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_DELETEMENU].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		args = AS3_Array("");
		AS3_Call(qspCallBacks[QSP_CALL_DELETEMENU].FuncVal, qspCallBacks[QSP_CALL_DELETEMENU].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #8
0
void qspCallRefreshInt(QSP_BOOL isRedraw)
{
	/* Здесь выполняем обновление интерфейса */
	QSPCallState state;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_REFRESHINT].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		args = AS3_Array("IntType", isRedraw);
		AS3_Call(qspCallBacks[QSP_CALL_REFRESHINT].FuncVal, qspCallBacks[QSP_CALL_REFRESHINT].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #9
0
void qspCallSetTimer(int msecs)
{
	/* Здесь устанавливаем интервал таймера */
	QSPCallState state;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_SETTIMER].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		args = AS3_Array("IntType", msecs);
		AS3_Call(qspCallBacks[QSP_CALL_SETTIMER].FuncVal, qspCallBacks[QSP_CALL_SETTIMER].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #10
0
/*
  TODO: Ugly workaround. Remove.
        See http://tinyurl.com/a9djb2
*/
BOOL is_null(AS3_Val val)
{
  BOOL result = FALSE;
  AS3_Val argsVal = AS3_Array("AS3ValType", val);
  AS3_Val classNameVal = AS3_Call(getQualifiedClassName_method, NULL, argsVal);
  AS3_Malloced_Str className = AS3_StringValue(classNameVal);
  AS3_Release(argsVal);
  AS3_Release(classNameVal);

  result = (strncmp(className, "null", 4) == 0);

  free(className);

  return result;
}
Beispiel #11
0
void qspCallShowWindow(int type, QSP_BOOL isShow)
{
	/* Здесь показываем или скрываем окно */
	QSPCallState state;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_SHOWWINDOW].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		args = AS3_Array("IntType, IntType", type, isShow);
		AS3_Call(qspCallBacks[QSP_CALL_SHOWWINDOW].FuncVal, qspCallBacks[QSP_CALL_SHOWWINDOW].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #12
0
void qspCallShowMenu()
{
	/* Здесь показываем меню */
	QSPCallState state;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_SHOWMENU].IsSet)
	{
		qspSaveCallState(&state, QSP_FALSE, QSP_TRUE);
		args = AS3_Array("");
		AS3_Call(qspCallBacks[QSP_CALL_SHOWMENU].FuncVal, qspCallBacks[QSP_CALL_SHOWMENU].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #13
0
int qspCallShowMenu()
{
	/* «десь показываем меню */
	QSPCallState state;
	int index;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_SHOWMENU].IsSet)
	{
		qspSaveCallState(&state, QSP_FALSE, QSP_TRUE);
		args = AS3_Array("");
		AS3_Call(qspCallBacks[QSP_CALL_SHOWMENU].FuncVal, qspCallBacks[QSP_CALL_SHOWMENU].ThisVal, args);
		AS3_Release(args);
		flyield();
		index = AS3_IntValue(result);
		AS3_Release(result);
		qspRestoreCallState(&state);
		return index;
	}
	return -1;
}
Beispiel #14
0
int qspCallGetMSCount()
{
	/* Здесь получаем количество миллисекунд, прошедших с момента последнего вызова функции */
	QSPCallState state;
	int count;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_GETMSCOUNT].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		args = AS3_Array("");
		AS3_Call(qspCallBacks[QSP_CALL_GETMSCOUNT].FuncVal, qspCallBacks[QSP_CALL_GETMSCOUNT].ThisVal, args);
		AS3_Release(args);
		flyield();
		count = AS3_IntValue(result);
		AS3_Release(result);
		qspRestoreCallState(&state);
		return count;
	}
	return 0;
}
Beispiel #15
0
void qspCallAddMenuItem(QSP_CHAR *name, QSP_CHAR *imgPath)
{
	/* Здесь добавляем пункт меню */
	QSPCallState state;
	AS3_Val args;
	char *nameUTF8;
	char *imgUTF8;
	if (qspCallBacks[QSP_CALL_ADDMENUITEM].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		nameUTF8 = (name ? qspW2C(name) : 0);
		imgUTF8 = (imgPath ? qspW2C(imgPath) : 0);
		args = AS3_Array("StrType, StrType", nameUTF8, imgUTF8);
		if (nameUTF8) free(nameUTF8);
		if (imgUTF8) free(imgUTF8);
		AS3_Call(qspCallBacks[QSP_CALL_ADDMENUITEM].FuncVal, qspCallBacks[QSP_CALL_ADDMENUITEM].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #16
0
void qspCallDebug(QSP_CHAR *str)
{
	/* Здесь передаем управление отладчику */
	QSPCallState state;
	char *strUTF8;
	AS3_Val args;
	if (qspCallBacks[QSP_CALL_DEBUG].IsSet)
	{
		qspSaveCallState(&state, QSP_FALSE, QSP_FALSE);
		if (str)
		{
			strUTF8 = qspW2C(str);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_DEBUG].FuncVal, qspCallBacks[QSP_CALL_DEBUG].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #17
0
void qspCallSetInputStrText(QSP_CHAR *text)
{
	/* Здесь устанавливаем текст строки ввода */
	QSPCallState state;
	AS3_Val args;
	char *textUTF8;
	if (qspCallBacks[QSP_CALL_SETINPUTSTRTEXT].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		if (text)
		{
			textUTF8 = qspW2C(text);
			args = AS3_Array("StrType", textUTF8);
			free(textUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_SETINPUTSTRTEXT].FuncVal, qspCallBacks[QSP_CALL_SETINPUTSTRTEXT].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #18
0
void qspCallCloseFile(QSP_CHAR *file)
{
	/* Здесь выполняем закрытие файла */
	QSPCallState state;
	AS3_Val args;
	char *strUTF8;
	if (qspCallBacks[QSP_CALL_CLOSEFILE].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		if (file)
		{
			strUTF8 = qspW2C(file);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_CLOSEFILE].FuncVal, qspCallBacks[QSP_CALL_CLOSEFILE].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #19
0
void qspCallSystem(QSP_CHAR *cmd)
{
	/* Здесь выполняем системный вызов */
	QSPCallState state;
	AS3_Val args;
	char *strUTF8;
	if (qspCallBacks[QSP_CALL_SYSTEM].IsSet)
	{
		qspSaveCallState(&state, QSP_FALSE, QSP_FALSE);
		if (cmd)
		{
			strUTF8 = qspW2C(cmd);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_SYSTEM].FuncVal, qspCallBacks[QSP_CALL_SYSTEM].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #20
0
void qspCallPlayFile(QSP_CHAR *file, int volume)
{
	/* Здесь начинаем воспроизведение файла с заданной громкостью */
	QSPCallState state;
	AS3_Val args;
	char *strUTF8;
	if (qspCallBacks[QSP_CALL_PLAYFILE].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		if (file)
		{
			strUTF8 = qspW2C(file);
			args = AS3_Array("StrType, IntType", strUTF8, volume);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType, IntType", 0, volume);
		AS3_Call(qspCallBacks[QSP_CALL_PLAYFILE].FuncVal, qspCallBacks[QSP_CALL_PLAYFILE].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #21
0
void qspCallShowMessage(QSP_CHAR *text)
{
	/* Здесь показываем сообщение */
	QSPCallState state;
	AS3_Val args;
	char *strUTF8;
	if (qspCallBacks[QSP_CALL_SHOWMSGSTR].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		if (text)
		{
			strUTF8 = qspW2C(text);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_SHOWMSGSTR].FuncVal, qspCallBacks[QSP_CALL_SHOWMSGSTR].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #22
0
void qspCallShowPicture(QSP_CHAR *file)
{
	/* Здесь показываем изображение */
	QSPCallState state;
	AS3_Val args;
	char *strUTF8;
	if (qspCallBacks[QSP_CALL_SHOWIMAGE].IsSet)
	{
		qspSaveCallState(&state, QSP_TRUE, QSP_FALSE);
		if (file)
		{
			strUTF8 = qspW2C(file);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_SHOWIMAGE].FuncVal, qspCallBacks[QSP_CALL_SHOWIMAGE].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #23
0
void qspCallOpenGame(QSP_CHAR *file)
{
	/* Здесь позволяем пользователю выбрать файл */
	/* состояния игры для загрузки и загружаем его */
	QSPCallState state;
	AS3_Val args;
	char *strUTF8;
	if (qspCallBacks[QSP_CALL_OPENGAMESTATUS].IsSet)
	{
		qspSaveCallState(&state, QSP_FALSE, QSP_TRUE);
		if (file)
		{
			strUTF8 = qspW2C(file);
			args = AS3_Array("StrType", strUTF8);
			free(strUTF8);
		}
		else
			args = AS3_Array("StrType", 0);
		AS3_Call(qspCallBacks[QSP_CALL_OPENGAMESTATUS].FuncVal, qspCallBacks[QSP_CALL_OPENGAMESTATUS].ThisVal, args);
		AS3_Release(args);
		flyield();
		qspRestoreCallState(&state);
	}
}
Beispiel #24
0
/*
* Take the Lua stack item at index i and convert it into an
* ActionScript value.
*/
AS3_Val get_as3_value_from_lua_stack_type(lua_State * L, int i, int type)
{
  /* WARNING: Panic alert! Use L*_FN checkers here! */

  LCALL(L, stack);

  AS3_Val value;
  switch (type)
  {
    case LUA_TSTRING:  /* strings */
      {
        size_t length = 0;
        const char * str = lua_tolstring(L, i, &length);
        if (str == NULL)  /* NOTE: This is unreachable. Assert instead */
        {
          length = 6;
          str = "(null)";
        }
        /* NOTE: Alchemy .5a truncates embedded zeroes in string
        * regardless to the passed length
        */
        value = AS3_StringN(str, length);
      }
      break;

    case LUA_TBOOLEAN:  /* booleans */
      value = lua_toboolean(L, i) ? AS3_True() : AS3_False();
      break;

    case LUA_TNUMBER:  /* numbers */
      value = AS3_Number(lua_tonumber(L, i));
      break;

    case LUA_TNONE: /* fall through */
    case LUA_TNIL:  /* nil */
      value = AS3_Null();
      break;

    case LUA_TUSERDATA:  /* userdata */
      {
        void * userdata = lua_touserdata(L, i);
        lua_getfield(L, LUA_REGISTRYINDEX, AS3LUA_METATABLE);
        if (userdata == NULL || !lua_getmetatable(L, i))
        {
          lua_pop(L, 1); /* Pop AS3LUA_METATABLE */
          value = as3_value_from_foreign_userdata(L, i);
        }
        else if (!lua_rawequal(L, -2, -1))
        {
          lua_pop(L, 2); /* Pop AS3LUA_METATABLE and userdata metatable */
          value = as3_value_from_foreign_userdata(L, i);
        }
        else
        {
          lua_pop(L, 2); /* Pop AS3LUA_METATABLE and userdata metatable */
          AS3LuaUserData * userdata = (AS3LuaUserData *)lua_touserdata(L, i);
          value = userdata->value;
          /*
          * We just created one more reference to the AS3 value,
          * as it still lives inside Lua.
          * (And will probably be collected by GC.)
          */
          AS3_Acquire(value);
        }
      }
      break;

    case LUA_TFUNCTION: /* function */
      value = setup_callback(L, i);
      break;

    case LUA_TLIGHTUSERDATA: /* TODO: blackbox this type */
    case LUA_TTABLE: /* TODO: deal with this type */
    case LUA_TTHREAD: /* TODO: blackbox this type */
      value = AS3_String(lua_typename(L, type));
      break;

    default:  /* unreachable */
      fatal_error("unknown Lua type");
      break;
  }

#ifdef DO_SPAM
  SPAM(("get_as3_value_from_lua_stack(): end"));
  AS3_Trace(
      AS3_Call(
          getQualifiedClassName_method, NULL, AS3_Array("AS3ValType", value)
        )
    );
#endif /* DO_SPAM */

  LCHECK_FN(L, stack, 0, fatal_error);

  return value;
}
Beispiel #25
0
void errorToFlash(const char* message)
{	
	AS3_Val trace = AS3_NSGetS(NULL, "trace");
	AS3_Val params = AS3_Array("StrType", message);
	AS3_Release(AS3_Call(trace, AS3_Undefined(), params));
}
Beispiel #26
0
/*
* Function used as a callback for all Lua functions passed through
* get_as3_value_from_lua_stack()
*/
AS3_Val as3_lua_callback(void * data, AS3_Val args)
{
  /* WARNING: Panic alert! Use L*_FN checkers here! */

  SPAM(("as3_lua_callback(): begin"));

  AS3_Val res;
  LuaFunctionCallbackData * func_data = (LuaFunctionCallbackData *) data;
  int nargs = 0;
  int status = 0;
  int results_base = 0;
  lua_State * L = func_data->L;
  if (L == NULL)
  {
    /* TODO: Should we crash here?
    fatal_error("state expired"); / * Does not return * /
    */
    sztrace("as3_lua_callback: state expired");
    return AS3_Undefined();
  }

  { /* A new scope for LCALL to work (C89 conformance) */
    LCALL(L, stack);

    /* TODO: Cache that with lua_ref, it is faster */
    lua_getfield(L, LUA_REGISTRYINDEX, AS3LUA_CALLBACKS);

    /* TODO: Assert we have a table here */

    lua_rawgeti(L, -1, func_data->ref); /* push stored function */

    if (lua_istable(L, -1) == 0) /* Probably nil */
    {
      lua_pop(L, 1); /* Pop bad callback table */
      LCHECK_FN(L, stack, 0, fatal_error);

      fatal_error("function callback not found"); /* Does not return */
    }

    lua_rawgeti(L, -1, AS3LUA_CBFNINDEX); /* push stored callback function */

 #ifdef DO_SPAM
  {
    SPAM(("as3_lua_callback(): AS3 arguments"));
    AS3_Val a = AS3_CallS("join", args, AS3_Undefined());
    AS3_Trace(a);
    SAFE_RELEASE(a);
  }
#endif /* DO_SPAM */


    /* TODO: Assert we have Lua function (or other callable object) on the top of the stack */

    LCHECK_FN(L, stack, 2 + 1, fatal_error);

    nargs = push_as3_array_to_lua_stack(L, args); /* push arguments */

#ifdef DO_SPAM
    /* TODO: Remove */
    lua_pushcfunction(L, as3_trace);
    dump_lua_stack(L, LBASE(L, stack) + 2 + 1);
    lua_pushliteral(L, "ARGUMENTS");
    lua_pushnumber(L, nargs);
    lua_call(L, 3, 0);
#endif /* DO_SPAM */

    LCHECK_FN(L, stack, 2 + 1 + nargs, fatal_error);

    results_base = LBASE(L, stack) + 2;
    status = do_pcall_with_traceback(L, nargs, LUA_MULTRET);
    if (status != 0)
    {
      const char * msg = NULL;

      LCHECK_FN(L, stack, 2 + 1, fatal_error); /* Tables and error message */
      lua_remove(L, -2); /* Remove AS3LUA_CALLBACKS table */
      lua_remove(L, -2); /* Remove holder table */
      LCHECK_FN(L, stack, 1, fatal_error); /* Only error message */

      /* Error message is on stack */
      /* NOTE: It is not necessary string! If we want to preserve its type, see lua_DoString. */

      if (lua_tostring(L, -1) == NULL)
      {
        lua_pop(L, 1);
        lua_pushliteral(L, "(non-string)");
      }

      LCHECK_FN(L, stack, 1, fatal_error);

      lua_pushliteral(L, "Error in Lua callback:\n");
      lua_insert(L, -2);

      LCHECK_FN(L, stack, 2, fatal_error);

      lua_concat(L, 2);

      LCHECK_FN(L, stack, 1, fatal_error);

      sztrace((char *)lua_tostring(L, -1));

      /* TODO: ?! */
      /* lua_error(L); */

      msg = lua_tostring(L, -1);
      lua_pop(L, 1);

/*
      fatal_error(msg); / * Does not return * /
*/
    }

    /* Process results */

#ifdef DO_SPAM
    /* TODO: Remove */
    /*
    lua_pushcfunction(L, as3_trace);
    lua_pushliteral(L, "STACK");
    dump_lua_stack(L, results_base);
    lua_call(L, 2, 0);
    */
#endif /* DO_SPAM */

    res = create_as3_value_from_lua_stack(L, results_base + 1, LTOP(L, stack), TRUE);

#ifdef DO_SPAM
    SPAM(("as3_lua_callback() result type"));
    AS3_Trace(AS3_Call(getQualifiedClassName_method, NULL, AS3_Array("AS3ValType", res)));
#endif /* DO_SPAM */

    lua_settop(L, LBASE(L, stack)); /* Cleanup results and two holder tables */

    SPAM(("as3_lua_callback(): end"));

    return res;
  }

  /* Unreachable */
}