#include "stdafx.h"
#include <iostream>
#include <string.h>
#include <stdarg.h>
using namespace std;
#include "UtilityFunctions.h"
//------- Helper functions for Lua table variables
// The following functions are used to push table entries onto
// the stack so a table can be created.
// keys are of two types, integer keys being numbers starting with 1 or
// string keys being strings for an association between the key and some value.
void LuaSimpleWrapper::l_pushtablestring(lua_State *lua, char* key , char* value) {
lua_pushstring(lua, key);
// standard char zero terminated string so Lua can figure out the size
lua_pushstring(lua, value);
lua_settable(lua, -3);
}
void LuaSimpleWrapper::l_pushtablestringW(lua_State *lua, char* key , wchar_t *value) {
size_t iSize = 0;
for (iSize = 0; value[iSize]; iSize++);
iSize++; // include the end of string terminator
iSize *= sizeof(wchar_t); // change from number of characters to number of bytes
lua_pushstring(lua, key);
// non-standard multi-byte string so we must specify the size of the string of bytes
lua_pushlstring(lua, (const char *)value, iSize);
lua_settable(lua, -3);
}
void LuaSimpleWrapper::l_pushtablenumber(lua_State *lua, char* key, double value) {
lua_pushstring(lua, key);
lua_pushnumber(lua, value);
lua_settable(lua, -3);
}
void LuaSimpleWrapper::l_pushtableinteger(lua_State *lua, char* key, lua_Integer value) {
lua_pushstring(lua, key);
lua_pushinteger(lua, value);
lua_settable(lua, -3);
}
void LuaSimpleWrapper::l_pushtablestring(lua_State *lua, lua_Integer key, char* value) {
lua_pushinteger(lua, key);
lua_pushstring(lua, value);
lua_settable(lua, -3);
}
void LuaSimpleWrapper::l_pushtablefunctionCharKey(lua_State *lua, char* key, lua_CFunction value) {
lua_pushstring(lua, key);
lua_pushcclosure (lua, value, 0);
lua_settable(lua, -3);
}
int LuaSimpleWrapper::PutObjectFrameData (const lua_Integer objectindex, const lua_Integer frameindex, const char *aszTableName)
{
m_ListOfObjects[objectindex]->m_ListOfFrames[frameindex].iValue = (int)frameindex;
strcpy_s (m_ListOfObjects[objectindex]->m_ListOfFrames[frameindex].aszName, sizeof(m_ListOfObjects[objectindex]->m_ListOfFrames[frameindex].aszName), aszTableName);
return 0;
}
//------------------------------------------------------
extern "C" {
static int ParserLuaCreateGlobalFrame (lua_State *luaState = 0);
};
LuaSimpleWrapper::LuaSimpleWrapper (bool newState) : m_luaState(0), m_iStartNewStateRetStatus(0), m_lastState(LuaNewInit)
{
m_lastLuaError[0] = 0;
m_MyObjectCount = LuaSimpleWrapper::m_ObjectCount++;
LuaSimpleWrapper::m_ListOfObjects[m_MyObjectCount] = this;
if (newState) {
StartNewState (true);
}
}
LuaSimpleWrapper::~LuaSimpleWrapper ()
{
if (m_luaState) {
lua_close(m_luaState);
m_luaState = 0;
}
}
int LuaSimpleWrapper::StartNewState (bool openLibs)
{
// initialize the lua interpreter for this thread
// we could use lua_State *L = luaL_newstate(); instead
// however with lua_newstate() we have additional options.
// when we are done we will use lua_close()
if (m_luaState == 0) {
m_luaState = lua_newstate(ParserLuaAlloc, 0);
if (m_luaState) {
m_iStartNewStateRetStatus = 1;
if (openLibs) {
m_iStartNewStateRetStatus = 2;
// open up the standard library for use
luaL_openlibs (m_luaState);
}
}
}
return m_iStartNewStateRetStatus;
}
int LuaSimpleWrapper::LoadLuaFile (char *aszFilePath)
{
int iRetStatus = 0;
if (m_luaState) {
// should return 0 if successful otherwise will return the following:
// LUA_ERRFILE unable to open the file from luaL_loadfile()
// LUA_ERRSYNTAX syntax error in the lua code in the file from lua_load()
// LUA_ERRMEM if there is a memory allocation error from lua_load()
// the loadfile function loads a lua script chunk or section of source
// into the lua script engine along with any other pieces that are there.
// it does not execute any script, it only load the chunk.
int iStatus = luaL_loadfile(m_luaState, aszFilePath);
if (iStatus != 0) {
strncpy_s (m_lastLuaError, sizeof(m_lastLuaError), lua_tostring(m_luaState, -1), sizeof(m_lastLuaError)/sizeof(m_lastLuaError[0]));
m_lastLuaError[sizeof(m_lastLuaError)/sizeof(m_lastLuaError[0]) - 1] = 0;
m_lastState = LuaLoadFile;
iRetStatus = -1;
}
} else {
strcpy_s (m_lastLuaError, sizeof(m_lastLuaError), "Lua state not initialized.");
m_lastState = LuaLoadFile;
iRetStatus = -2;
}
return iRetStatus;
}
int LuaSimpleWrapper::LoadLuaFile (wchar_t *wszFilePath)
{
char tempFilePath[2048];
int i;
for (i = 0; i < 2040 && wszFilePath[i]; i++) tempFilePath[i] = (char) wszFilePath[i];
tempFilePath[i] = 0;
return LoadLuaFile (tempFilePath);
}
char const *LuaSimpleWrapper::GetLastErrorString(char *aszError, size_t bufLen)
{
if (aszError && bufLen > 0) {
strncpy_s (aszError, bufLen, m_lastLuaError, bufLen);
aszError[bufLen - 1] = 0;
}
return &m_lastLuaError[0];
}
lua_State const *LuaSimpleWrapper::GetLuaState (void)
{
return m_luaState;
}
int LuaSimpleWrapper::m_ObjectCount = 0;
LuaSimpleWrapper *LuaSimpleWrapper::m_ListOfObjects[20] = {0};
int LuaSimpleWrapper::InitLuaEnvironment (int (*pFunc)(LuaSimpleWrapper *luaWrapper, lua_State *luaState))
{
int iRetStatus = 0;
if (m_luaState) {
// CreateFrame() function that will create a frame with an index
// This function uses two variables which are used to store the
// frame data allowing it to be used by the application in order to
// send events to a specific frame object in the Lua code.
// we access the array of the list of objects, m_ListOfObjects[], with objectindex
// and we access the specific frame for the object with frameindex.
lua_pushnumber(m_luaState, 0); // frameindex, count of frames for this Lua state object, init to zero
lua_pushnumber(m_luaState, m_MyObjectCount); // objectindex, which Lua state object am I?
// create the C closure with the above two arguments,
lua_pushcclosure (m_luaState, ParserLuaCreateGlobalFrame, 2);
lua_setglobal (m_luaState, "CreateFrame");
if (pFunc) {
iRetStatus = pFunc (this, m_luaState);
}
if (iRetStatus >= 0) {
// should return 0 if successful otherwise will return the following:
// LUA_ERRRUN unable to run the script.
// this initial call will run the script loaded and prep the various functions
// and variables. It is necessary to call this once in order to run the script
// so that all the global variables for functions and table names will be set.
// after this call, we can perform various actions by setting up the Lua stack
// and then using lua_pcall() again to perform the action.
int iStatus = lua_pcall(m_luaState, 0, 0, 0);
if (iStatus != 0) {
strncpy_s (m_lastLuaError, sizeof(m_lastLuaError), lua_tostring(m_luaState, -1), sizeof(m_lastLuaError)/sizeof(m_lastLuaError[0]));
m_lastLuaError[sizeof(m_lastLuaError)/sizeof(m_lastLuaError[0]) - 1] = 0;
m_lastState = LuaInitLoad;
iRetStatus = -1;
}
}
} else {
strcpy_s (m_lastLuaError, sizeof(m_lastLuaError), "Lua state not initialized.");
m_lastState = LuaInitLoad;
iRetStatus = -2;
}
return iRetStatus;
}
// TriggerGlobalCall() is a method to allow an application program to invoke
// a function in a global variable. this function uses a variable argument
// list to allow the application to specify as many or as few arguments as
// needed. to use this method you must specify a description of the call
// along with a list of the parameters.
//
// the description has the following format.
// "global.item:d,s,i"
// global -> name of a global variable
// key -> name of a table key (optional)
// d -> double number value
// s -> zero terminated char type string
// i -> integer number value
int LuaSimpleWrapper::TriggerGlobalCall (char *aszDescription, ...)
{
char tempStringBuffer[2048];
char *globalName = 0;
char *globalKey = 0;
char *pString = tempStringBuffer;
int iStatus = 0;
int iState = 1;
int nPushCount = 0;
va_list argList;
va_start (argList, aszDescription);
strncpy_s (tempStringBuffer, sizeof(tempStringBuffer), aszDescription, sizeof(tempStringBuffer)/sizeof(tempStringBuffer[0]));
tempStringBuffer[sizeof(tempStringBuffer)/sizeof(tempStringBuffer[0]) - 1] = 0;
while (*pString) {
switch (iState) {
case 1:
if (*pString != ' ')
iState = 2;
else
break;
// if we have found a non-space then fall through to state 2
// so that we can process the first non-space character.
case 2:
if (*pString == ':') {
*pString = 0;
iState = 3;
if (globalName && *globalName) {
// get the global onto the Lua virtual stack by querying the Lua engine
// then we check to see if the value provided is nil indicating that the
// global does not exist. if so we error out.
lua_getglobal (m_luaState, globalName);
if (lua_type(m_luaState, lua_gettop(m_luaState)) == LUA_TNIL) {
// if the global variable does not exist then we will bail out with an error.
strcpy_s (m_lastLuaError, sizeof(m_lastLuaError), "Global variable not found: ");
strcat_s (m_lastLuaError, sizeof(m_lastLuaError), globalName);
m_lastState = LuaDescripParse;
// error so we will just clear the Lua virtual stack and then return
// if we do not clear the Lua stack, we leave garbage that will cause
// problems with later function calls from the application.
// we do this rather than use lua_error() because this function is called
// from the application and not through Lua.
lua_settop (m_luaState, 0);
return -1;
}
}
break;
} else if (*pString == '.') {
*pString = 0;
iState = 12;
if (globalName && *globalName) {
// get the global onto the Lua virtual stack by querying the Lua engine
// then we check to see if the value provided is nil indicating that the
// global does not exist. if so we error out.
lua_getglobal (m_luaState, globalName);
if (lua_type(m_luaState, lua_gettop(m_luaState)) == LUA_TNIL) {
// if the global variable does not exist then we will bail out with an error.
strcpy_s (m_lastLuaError, sizeof(m_lastLuaError), "Global variable not found: ");
strcat_s (m_lastLuaError, sizeof(m_lastLuaError), globalName);
m_lastState = LuaDescripParse;
// error so we will just clear the Lua virtual stack and then return
// if we do not clear the Lua stack, we leave garbage that will cause
// problems with later function calls from the application.
// we do this rather than use lua_error() because this function is called
// from the application and not through Lua.
lua_settop (m_luaState, 0);
return -1;
}
}
break;
}
if (globalName == 0) globalName = pString;
break;
case 12:
if (*pString == ':') {
*pString = 0;
iState = 3;
if (globalKey) {
// the global is already on the Lua virtual stack so lets do a
// query of the Lua engine to see if this is a table and the key exists.
// if the value provided by the Lua engine is nil the then the key
// specified does not exist in the global table so we error out.
if (lua_type(m_luaState, lua_gettop(m_luaState)) != LUA_TTABLE) {
// if the global table does not exist then we will bail out with an error.
strcpy_s (m_lastLuaError, sizeof(m_lastLuaError), "Global table not found: ");
strcat_s (m_lastLuaError, sizeof(m_lastLuaError), globalName);
strcat_s (m_lastLuaError, sizeof(m_lastLuaError), ".");
strcat_s (m_lastLuaError, sizeof(m_lastLuaError), globalKey);
m_lastState = LuaDescripParse;
// error so we will just clear the Lua virtual stack and then return
// if we do not clear the Lua stack, we leave garbage that will cause
// problems with later function calls from the application.
// we do this rather than use lua_error() because this function is called
// from the application and not through Lua.
lua_settop (m_luaState, 0);
return -1;
}
lua_pushstring (m_luaState, globalKey);
lua_gettable (m_luaState, 1);
if (lua_type(m_luaState, lua_gettop(m_luaState)) == LUA_TNIL) {
// if the table key does not exist then we will bail out with an error.
strcpy_s (m_lastLuaError, sizeof(m_lastLuaError), "Global variable key not found: ");
strcat_s (m_lastLuaError, sizeof(m_lastLuaError), globalName);
strcat_s (m_lastLuaError, sizeof(m_lastLuaError), ".");
strcat_s (m_lastLuaError, sizeof(m_lastLuaError), globalKey);
m_lastState = LuaDescripParse;
// error so we will just clear the Lua virtual stack and then return
// if we do not clear the Lua stack, we leave garbage that will cause
// problems with later function calls from the application.
// we do this rather than use lua_error() because this function is called
// from the application and not through Lua.
lua_settop (m_luaState, 0);
return -1;
}
}
lua_insert (m_luaState, 1);
nPushCount++;
}
if (globalKey == 0 && *pString != ' ') globalKey = pString;
break;
case 3:
// process the parameter list stepping through the descriptor
// and processing the function parameter list. this is handling
// the variable part of the parameter list.
switch (*pString) {
case 'd': // the parameter being passed is a double
case 'D':
{
double dVal = va_arg(argList, double);
lua_pushnumber (m_luaState, dVal);
nPushCount++;
}
break;
case 'f': // the parameter being passed is a C function
case 'F':
{
lua_CFunction fVal = va_arg(argList, lua_CFunction);
lua_pushcclosure (m_luaState, fVal, 0);
nPushCount++;
}
break;
case 'i': // the parameter being passed is an integer
case 'I':
{
int iVal = va_arg(argList, int);
lua_pushinteger (m_luaState, iVal);
nPushCount++;
}
break;
case 's': // the parameter being passed is a zero terminated string
case 'S':
{
char *asz = va_arg(argList, char *);
lua_pushstring (m_luaState, asz);
nPushCount++;
}
break;
case 'w': // the parameter being passed is a string of bytes
case 'W':
{
wchar_t *asz = va_arg(argList, wchar_t *);
size_t iLen = wcslen(asz);
iLen += 1; // add one to include the zero termination character
iLen *= sizeof(wchar_t); // figure number of bytes in the string.
lua_pushlstring (m_luaState, (char *)asz, iLen);
nPushCount++;
}
break;
default:
break;
}
break;
default:
break;
}
pString++;
}
iStatus = lua_pcall(m_luaState, nPushCount, 0, 0);
if (iStatus != 0) {
strncpy_s (m_lastLuaError, sizeof(m_lastLuaError), lua_tostring(m_luaState, -1), sizeof(m_lastLuaError)/sizeof(m_lastLuaError[0]));
m_lastLuaError[sizeof(m_lastLuaError)/sizeof(m_lastLuaError[0]) - 1] = 0;
m_lastState = LuaGlobalCall;
iStatus = -2;
}
return iStatus;
}
static int UifLuaOnEvent (lua_State *lua)
{
int nPushCount = 0;
int argc = lua_gettop(lua);
const char *msg = lua_tostring (lua, 1);
cout << "UifLuaOnEvent() called. " << msg << endl;
return 0;
}
// RegisterEvent is called by specifying two arguments
// self -> the name of the table created by UifLuaCreateFrame()
// key -> the name of the event which is to be observed
static int UifLuaRegisterEvent (lua_State *lua)
{
int nPushCount = 0;
int argc = lua_gettop(lua);
const char *eventKey = lua_tostring (lua, 2);
int result = 0;
cout << "UifLuaRegisterEvent() called. " << eventKey << endl;
return 0;
}
static int ParserLuaCreateGlobalFrame (lua_State *luaState)
{
int nPushCount = 0;
if (luaState == 0)
return nPushCount;
int argc = lua_gettop(luaState);
const char *tableKey = lua_tostring (luaState, 1);
// get the two integer values that are part of the C closure for this function.
// the first integer value is the frame count which is incremented for each frame created.
// the second integer value is the object count which is increment for each object created.
// an object may create multiple frames as requested by the Lua script it is executing.
lua_Integer frameindex = lua_tointeger(luaState, lua_upvalueindex(1));
lua_Integer objectindex = lua_tointeger(luaState, lua_upvalueindex(2));
// the CreateFrame closure contains an upvalue or local variable
// associated with the CreateFrame closure which contains a unique
// identifier for each frame that is created.
lua_pushinteger(luaState, (frameindex+1)); /* new value */
lua_pushvalue(luaState, -1); /* duplicate it */
lua_replace(luaState, lua_upvalueindex(1)); /* update upvalue */
// create the Frame table with the necessary set of fields
// that are standard for a Frame table.
// we create the new table on the Lua virtual stack and then
// set the specific keys that we want to set with the values for
// those keys. Since there is only one value returned, the table,
// we increment the number of arguments in nPushCount by one.
lua_newtable(luaState);
LuaSimpleWrapper::l_pushtableinteger(luaState, "FrameIndex", frameindex);
LuaSimpleWrapper::l_pushtablefunctionCharKey(luaState, "OnEvent", UifLuaOnEvent);
LuaSimpleWrapper::l_pushtablefunctionCharKey(luaState, "RegisterEvent", UifLuaRegisterEvent);
// now call setglobal to create a global variable in the Lua environment
lua_setglobal (luaState, tableKey);
// now get the global so that we can return it in case the user expects the handle as
// part of calling the function to create the frame.
// for instance in Lua: myNewFrame = CreateFrame ("myNewFrame")
lua_getglobal (luaState, tableKey);
nPushCount++;
LuaSimpleWrapper::PutObjectFrameData (objectindex, frameindex, tableKey);
return nPushCount;
}
// The type of the memory-allocation function used by Lua states. The allocator function
// must provide a functionality similar to realloc, but not exactly the same. Its arguments
// are:
// ud, an opaque pointer passed to lua_newstate;
// ptr, a pointer to the block being allocated/reallocated/freed;
// osize, the original size of the block;
// nsize, the new size of the block.
//
// ptr is NULL if and only if osize is zero. When nsize is zero, the allocator
// must return NULL; if osize is not zero, it should free the block pointed to by ptr. When nsize
// is not zero, the allocator returns NULL if and only if it cannot fill the request. When nsize
// is not zero and osize is zero, the allocator should behave like malloc. When nsize and osize
// are not zero, the allocator behaves like realloc().
//
// Lua assumes that the allocator never fails when osize >= nsize.
//
// See also this documentation http://www.lua.org/manual/5.2/manual.html#lua_Alloc
//
void *ParserLuaAlloc (void *ud, void *ptr, size_t osize, size_t nsize)
{
void *pMalloc = NULL;
if (osize && nsize && ptr) {
if (osize < nsize) {
pMalloc = realloc (ptr, nsize);
} else {
pMalloc = ptr;
}
} else if (nsize) {
pMalloc = malloc (nsize);
} else if (nsize == 0) {
free (ptr);
pMalloc = NULL;
}
return pMalloc;
}
void LuaSimpleWrapper::ParserLuaSimpleStackDump (lua_State *L)
{
int i;
int top = lua_gettop(L);
cout << " Stack dump from 0 to top (" << top << ")\n ";
for (i = 0; i <= top; i++) { /* repeat for each level */
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING: /* strings */
cout << "`" << lua_tostring(L, i) << "'";
break;
case LUA_TBOOLEAN: /* booleans */
cout << (lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNUMBER: /* numbers */
cout << lua_tonumber(L, i);
break;
case LUA_TTABLE:
cout << lua_typename(L, t);
break;
default: /* other values */
cout << lua_typename(L, t);
break;
}
cout << " "; /* put a separator */
}
cout << endl; /* end the listing */
}