2012-11-10 20:59:00 +00:00
|
|
|
/* The Lunatic Interpreter, part of EDuke32. Common, engine-side stuff. */
|
|
|
|
|
|
|
|
#include <lua.h>
|
|
|
|
#include <lualib.h>
|
|
|
|
#include <lauxlib.h>
|
|
|
|
|
|
|
|
#include "cache1d.h"
|
|
|
|
#include "osd.h"
|
|
|
|
|
|
|
|
#include "lunatic.h"
|
|
|
|
|
|
|
|
|
|
|
|
////////// HELPER FUNCTIONS //////////
|
|
|
|
|
|
|
|
// Lua-registry key for debug.traceback
|
|
|
|
static uint8_t debug_traceback_key;
|
|
|
|
|
|
|
|
void L_CheckAndRegisterFunction(lua_State *L, void *regkeyaddr)
|
|
|
|
{
|
|
|
|
luaL_checktype(L, -1, LUA_TFUNCTION);
|
|
|
|
|
|
|
|
lua_pushlightuserdata(L, regkeyaddr); // 3, push address
|
|
|
|
lua_pushvalue(L, -2); // 4, push copy of lua function
|
|
|
|
|
|
|
|
lua_settable(L, LUA_REGISTRYINDEX); // "registry[regkeyaddr] = <lua function>", pop 2
|
|
|
|
}
|
|
|
|
|
2013-01-01 15:24:11 +00:00
|
|
|
static void L_SetupDebugTraceback(lua_State *L)
|
2012-11-10 20:59:00 +00:00
|
|
|
{
|
|
|
|
// get debug.traceback
|
|
|
|
lua_getglobal(L, "debug");
|
|
|
|
lua_getfield(L, -1, "traceback");
|
|
|
|
Bassert(lua_isfunction(L, -1));
|
|
|
|
L_CheckAndRegisterFunction(L, &debug_traceback_key);
|
|
|
|
lua_pop(L, 2);
|
|
|
|
}
|
|
|
|
|
2012-12-29 15:21:32 +00:00
|
|
|
void L_PushDebugTraceback(lua_State *L)
|
|
|
|
{
|
|
|
|
// get debug.traceback
|
|
|
|
lua_pushlightuserdata(L, &debug_traceback_key);
|
|
|
|
lua_gettable(L, LUA_REGISTRYINDEX);
|
|
|
|
Bassert(lua_isfunction(L, -1));
|
|
|
|
}
|
|
|
|
|
2012-11-10 20:59:00 +00:00
|
|
|
|
|
|
|
static int32_t read_whole_file(const char *fn, char **retbufptr)
|
|
|
|
{
|
|
|
|
int32_t fid, flen, i;
|
|
|
|
char *buf;
|
|
|
|
|
|
|
|
*retbufptr = NULL;
|
|
|
|
|
|
|
|
fid = kopen4load(fn, 0); // TODO: g_loadFromGroupOnly, kopen4loadfrommod ?
|
|
|
|
|
|
|
|
if (fid < 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
flen = kfilelength(fid);
|
|
|
|
if (flen == 0)
|
|
|
|
return 5;
|
|
|
|
|
|
|
|
buf = Bmalloc(flen+1);
|
|
|
|
if (!buf)
|
|
|
|
{
|
|
|
|
kclose(fid);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = kread(fid, buf, flen);
|
|
|
|
kclose(fid);
|
|
|
|
|
|
|
|
if (i != flen)
|
|
|
|
{
|
|
|
|
Bfree(buf);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[flen] = 0;
|
|
|
|
*retbufptr = buf;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////// EXTERNAL FUNCTIONS //////////
|
|
|
|
|
|
|
|
// 0: success, <0: failure
|
|
|
|
int L_CreateState(L_State *estate, const char *name, void (*StateSetupFunc)(lua_State *))
|
|
|
|
{
|
|
|
|
lua_State *L;
|
|
|
|
|
|
|
|
estate->name = Bstrdup(name);
|
|
|
|
if (!estate->name)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
L = estate->L = luaL_newstate();
|
|
|
|
|
|
|
|
if (!estate->L)
|
|
|
|
{
|
|
|
|
Bfree(estate->name);
|
|
|
|
estate->name = NULL;
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
2013-01-01 15:24:11 +00:00
|
|
|
luaL_openlibs(L);
|
|
|
|
L_SetupDebugTraceback(L);
|
|
|
|
if (StateSetupFunc)
|
|
|
|
StateSetupFunc(L);
|
|
|
|
|
|
|
|
if (lua_gettop(L)==0)
|
|
|
|
L_PushDebugTraceback(L);
|
|
|
|
// Otherwise, it is assumed that StateSetupFunc pushed a custom traceback
|
|
|
|
// function onto the stack.
|
|
|
|
|
|
|
|
Bassert(lua_gettop(L)==1);
|
2012-11-10 20:59:00 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void L_DestroyState(L_State *estate)
|
|
|
|
{
|
|
|
|
if (!estate->L)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Bfree(estate->name);
|
|
|
|
estate->name = NULL;
|
|
|
|
|
|
|
|
lua_close(estate->L);
|
|
|
|
estate->L = NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-01 15:24:11 +00:00
|
|
|
static void L_OnOutOfMem(void)
|
|
|
|
{
|
|
|
|
extern void uninitengine(void);
|
|
|
|
OSD_Printf("Out of memory in Lunatic.\n");
|
|
|
|
uninitengine();
|
|
|
|
exit(127);
|
|
|
|
}
|
|
|
|
|
2012-12-25 16:13:41 +00:00
|
|
|
void (*L_ErrorFunc)(const char *) = NULL;
|
2013-01-01 15:24:11 +00:00
|
|
|
void (*L_OutOfMemFunc)(void) = L_OnOutOfMem;
|
|
|
|
|
|
|
|
int L_HandleError(lua_State *L, int errcode, void (*ErrorPrintFunc)(const char *))
|
|
|
|
{
|
|
|
|
if (errcode == LUA_ERRMEM)
|
|
|
|
L_OutOfMemFunc();
|
|
|
|
|
|
|
|
if (errcode == LUA_ERRRUN || errcode == LUA_ERRERR)
|
|
|
|
{
|
|
|
|
if (lua_isboolean(L, -1))
|
|
|
|
{
|
|
|
|
int32_t killit = lua_toboolean(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return killit;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char *errstr = (lua_type(L, -1)==LUA_TSTRING) ?
|
|
|
|
lua_tostring(L, -1) : "??? (error message not a string)";
|
|
|
|
|
|
|
|
ErrorPrintFunc(errstr);
|
|
|
|
if (L_ErrorFunc)
|
|
|
|
L_ErrorFunc(errstr);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unreachable */
|
|
|
|
#ifdef NDEBUG
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
Bassert(0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void L_ErrorPrint(const char *errmsg)
|
|
|
|
{
|
|
|
|
OSD_Printf(OSD_ERROR "runtime error: %s\n", errmsg);
|
|
|
|
}
|
2012-11-10 20:59:00 +00:00
|
|
|
|
|
|
|
int L_RunString(L_State *estate, char *buf, int dofreebuf)
|
|
|
|
{
|
|
|
|
int32_t i;
|
|
|
|
lua_State *L = estate->L;
|
|
|
|
|
|
|
|
// -- lua --
|
2013-01-01 15:24:11 +00:00
|
|
|
Bassert(lua_gettop(L)==1); // on top: a traceback function
|
2012-11-10 20:59:00 +00:00
|
|
|
|
|
|
|
i = luaL_loadstring(L, buf);
|
|
|
|
Bassert(lua_gettop(L)==2);
|
|
|
|
if (dofreebuf)
|
|
|
|
Bfree(buf);
|
|
|
|
|
|
|
|
if (i == LUA_ERRMEM)
|
2013-01-01 15:24:11 +00:00
|
|
|
L_OutOfMemFunc();
|
2012-11-10 20:59:00 +00:00
|
|
|
|
|
|
|
if (i == LUA_ERRSYNTAX)
|
|
|
|
{
|
2012-12-25 16:13:41 +00:00
|
|
|
OSD_Printf(OSD_ERROR "state \"%s\" syntax error: %s\n", estate->name,
|
2012-11-10 20:59:00 +00:00
|
|
|
lua_tostring(L, -1)); // get err msg
|
2013-01-01 15:24:11 +00:00
|
|
|
lua_pop(L, 1); // pop errmsg
|
2012-11-10 20:59:00 +00:00
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
2013-01-01 15:24:11 +00:00
|
|
|
// call the lua chunk!
|
|
|
|
i = lua_pcall(L, 0, 0, 1);
|
|
|
|
Bassert(lua_gettop(L) == 1 + (i!=0));
|
2012-11-10 20:59:00 +00:00
|
|
|
|
2013-01-01 15:24:11 +00:00
|
|
|
if (i != 0)
|
|
|
|
L_HandleError(L, i, &L_ErrorPrint);
|
2012-11-10 20:59:00 +00:00
|
|
|
|
2013-01-01 15:24:11 +00:00
|
|
|
Bassert(lua_gettop(L)==1);
|
2012-11-10 20:59:00 +00:00
|
|
|
|
2013-01-01 15:24:11 +00:00
|
|
|
return i ? 4 : 0;
|
2012-11-10 20:59:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -1: alloc failure
|
|
|
|
// 0: success
|
|
|
|
// 1: didn't find file
|
|
|
|
// 2: couldn't read whole file
|
|
|
|
// 3: syntax error in lua file
|
|
|
|
// 4: runtime error while executing lua file
|
|
|
|
// 5: empty file
|
|
|
|
int L_RunOnce(L_State *estate, const char *fn)
|
|
|
|
{
|
|
|
|
char *buf;
|
|
|
|
int32_t i = read_whole_file(fn, &buf);
|
|
|
|
|
|
|
|
if (i != 0)
|
|
|
|
return i;
|
|
|
|
|
|
|
|
return L_RunString(estate, buf, 1);
|
|
|
|
}
|