BizHawk/LuaInterface/Lua/src/LuaDLL.cpp

933 lines
23 KiB
C++

using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::Reflection;
using namespace System::Collections;
using namespace System::Text;
using namespace System::Security;
// #include <vcclr.h>
// #define _WINNT_
// #include <WinDef.h>
#include <vcclr.h>
// #include <atlstr.h>
#include <stdio.h>
#using <mscorlib.dll>
#include <string.h>
#define LUA_BUILD_AS_DLL
#define LUA_LIB
#define LUA_CORE
#define lua_c
#define luac_c
#define loslib_c
extern "C"
{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
// Not sure of the purpose of this, but I'm keeping it -kevinh
static int tag = 0;
namespace Lua511
{
#if 1
#undef LUA_TNONE
#undef LUA_TNIL
#undef LUA_TNUMBER
#undef LUA_TSTRING
#undef LUA_TBOOLEAN
#undef LUA_TTABLE
#undef LUA_TFUNCTION
#undef LUA_TUSERDATA
#undef LUA_TLIGHTUSERDATA
/*
* Lua types for the API, returned by lua_type function
*/
public enum class LuaTypes
{
LUA_TNONE=-1,
LUA_TNIL=0,
LUA_TNUMBER=3,
LUA_TSTRING=4,
LUA_TBOOLEAN=1,
LUA_TTABLE=5,
LUA_TFUNCTION=6,
LUA_TUSERDATA=7,
LUA_TLIGHTUSERDATA=2
};
#endif
#if 1
#undef LUA_GCSTOP
#undef LUA_GCRESTART
#undef LUA_GCCOLLECT
#undef LUA_GCCOUNT
#undef LUA_GCCOUNTB
#undef LUA_GCSTEP
#undef LUA_GCSETPAUSE
#undef LUA_GCSETSTEPMUL
/*
* Lua Garbage Collector options (param "what")
*/
public enum class LuaGCOptions
{
LUA_GCSTOP = 0,
LUA_GCRESTART = 1,
LUA_GCCOLLECT = 2,
LUA_GCCOUNT = 3,
LUA_GCCOUNTB = 4,
LUA_GCSTEP = 5,
LUA_GCSETPAUSE = 6,
LUA_GCSETSTEPMUL = 7,
};
#endif
#undef LUA_REGISTRYINDEX
#undef LUA_ENVIRONINDEX
#undef LUA_GLOBALSINDEX
/*
* Special stack indexes
*/
public enum class LuaIndexes
{
LUA_REGISTRYINDEX=-10000,
LUA_ENVIRONINDEX=-10001,
LUA_GLOBALSINDEX=-10002
};
#if 0
/*
* Structure used by the chunk reader
*/
// [ StructLayout( LayoutKind.Sequential )]
public ref struct ReaderInfo
{
public String^ chunkData;
public bool finished;
};
/*
* Delegate for chunk readers used with lua_load
*/
public delegate String^ LuaChunkReader(IntPtr luaState, ReaderInfo ^data, uint size);
#endif
/*
* Delegate for functions passed to Lua as function pointers
*/
[System::Runtime::InteropServices::UnmanagedFunctionPointer(CallingConvention::Cdecl)]
public delegate int LuaCSFunction(IntPtr luaState);
// delegate for lua debug hook callback (by Reinhard Ostermeier)
[System::Runtime::InteropServices::UnmanagedFunctionPointer(CallingConvention::Cdecl)]
public delegate void LuaHookFunction(IntPtr luaState, IntPtr luaDebug);
// To fix the strings:
// http://support.microsoft.com/kb/311259
public ref class LuaDLL
{
// steffenj: BEGIN additional Lua API functions new in Lua 5.1
public:
#define toState ((lua_State *) luaState.ToPointer())
static int lua_gc(IntPtr luaState, LuaGCOptions what, int data)
{
return ::lua_gc(toState, (int) what, data);
}
static String^ lua_typename(IntPtr luaState, LuaTypes type)
{
return gcnew String(::lua_typename(toState, (int) type));
}
#undef luaL_typename
static String^ luaL_typename(IntPtr luaState, int stackPos)
{
return lua_typename(luaState, lua_type(luaState, stackPos));
}
static void luaL_error(IntPtr luaState, String^ message)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(message).ToPointer();
::luaL_error(toState, cs);
Marshal::FreeHGlobal(IntPtr(cs));
}
static void luaL_where(IntPtr luaState, int level)
{
::luaL_where(toState, level);
}
// Not yet wrapped
// static String^ luaL_gsub(IntPtr luaState, String^ str, String^ pattern, String^ replacement);
#if 0
// the functions below are still untested
static void lua_getfenv(IntPtr luaState, int stackPos);
static int lua_isfunction(IntPtr luaState, int stackPos);
static int lua_islightuserdata(IntPtr luaState, int stackPos);
static int lua_istable(IntPtr luaState, int stackPos);
static int lua_isuserdata(IntPtr luaState, int stackPos);
static int lua_lessthan(IntPtr luaState, int stackPos1, int stackPos2);
static int lua_rawequal(IntPtr luaState, int stackPos1, int stackPos2);
static int lua_setfenv(IntPtr luaState, int stackPos);
static void lua_setfield(IntPtr luaState, int stackPos, String^ name);
static int luaL_callmeta(IntPtr luaState, int stackPos, String^ name);
// steffenj: END additional Lua API functions new in Lua 5.1
#endif
// steffenj: BEGIN Lua 5.1.1 API change (lua_open replaced by luaL_newstate)
static IntPtr luaL_newstate()
{
return IntPtr(::luaL_newstate());
}
static void lua_close(IntPtr luaState)
{
::lua_close(toState);
}
// steffenj: BEGIN Lua 5.1.1 API change (new function luaL_openlibs)
static void luaL_openlibs(IntPtr luaState)
{
::luaL_openlibs(toState);
}
// Not yet wrapped
// static int lua_objlen(IntPtr luaState, int stackPos);
// steffenj: END Lua 5.1.1 API change (lua_strlen is now lua_objlen)
// steffenj: BEGIN Lua 5.1.1 API change (lua_doString^ is now a macro luaL_dostring)
static int luaL_loadstring(IntPtr luaState, String^ chunk)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(chunk).ToPointer();
int result = ::luaL_loadstring(toState, cs);
Marshal::FreeHGlobal(IntPtr(cs));
return result;
}
#undef luaL_dostring
static int luaL_dostring(IntPtr luaState, String^ chunk)
{
int result = luaL_loadstring(luaState, chunk);
if (result != 0)
return result;
return lua_pcall(luaState, 0, -1, 0);
}
/// <summary>DEPRECATED - use luaL_dostring(IntPtr luaState, string chunk) instead!</summary>
static int lua_dostring(IntPtr luaState, String^ chunk)
{
return luaL_dostring(luaState, chunk);
}
// steffenj: END Lua 5.1.1 API change (lua_dostring is now a macro luaL_dostring)
// steffenj: BEGIN Lua 5.1.1 API change (lua_newtable is gone, lua_createtable is new)
static void lua_createtable(IntPtr luaState, int narr, int nrec)
{
::lua_createtable(toState, narr, nrec);
}
#undef lua_newtable
static void lua_newtable(IntPtr luaState)
{
lua_createtable(luaState, 0, 0);
}
#undef luaL_dofile
// steffenj: END Lua 5.1.1 API change (lua_newtable is gone, lua_createtable is new)
// steffenj: BEGIN Lua 5.1.1 API change (lua_dofile now in LuaLib as luaL_dofile macro)
static int luaL_dofile(IntPtr luaState, String^ fileName)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(fileName).ToPointer();
int result = ::luaL_loadfile(toState, cs);
//CP: Free filename string before return to ensure a file that isnt found still has the string freed (submitted by paul moore)
//link: http://luaforge.net/forum/forum.php?thread_id=2825&forum_id=145
Marshal::FreeHGlobal(IntPtr(cs));
if (result != 0)
return result;
return ::lua_pcall(toState, 0, -1, 0);
}
#undef lua_getglobal
// steffenj: END Lua 5.1.1 API change (lua_dofile now in LuaLib as luaL_dofile)
static void lua_getglobal(IntPtr luaState, String^ name)
{
lua_pushstring(luaState, name);
::lua_gettable(toState, (int) LuaIndexes::LUA_GLOBALSINDEX);
}
#undef lua_setglobal
static void lua_setglobal(IntPtr luaState, String^ name)
{
lua_pushstring(luaState,name);
lua_insert(luaState,-2);
lua_settable(luaState, (int) LuaIndexes::LUA_GLOBALSINDEX);
}
static void lua_settop(IntPtr luaState, int newTop)
{
::lua_settop(toState, newTop);
}
#undef lua_pop
static void lua_pop(IntPtr luaState, int amount)
{
lua_settop(luaState, -(amount) - 1);
}
static void lua_insert(IntPtr luaState, int newTop)
{
::lua_insert(toState, newTop);
}
static void lua_remove(IntPtr luaState, int index)
{
::lua_remove(toState, index);
}
static void lua_gettable(IntPtr luaState, int index)
{
::lua_gettable(toState, index);
}
static void lua_rawget(IntPtr luaState, int index)
{
::lua_rawget(toState, index);
}
static void lua_settable(IntPtr luaState, int index)
{
::lua_settable(toState, index);
}
static void lua_rawset(IntPtr luaState, int index)
{
::lua_rawset(toState, index);
}
static IntPtr lua_newthread(IntPtr luaState)
{
return IntPtr(::lua_newthread(toState));
}
static int lua_resume(IntPtr luaState, int narg)
{
return ::lua_resume(toState, narg);
}
static int lua_yield(IntPtr luaState, int nresults)
{
return ::lua_yield(toState, nresults);
}
static void lua_setmetatable(IntPtr luaState, int objIndex)
{
::lua_setmetatable(toState, objIndex);
}
static int lua_getmetatable(IntPtr luaState, int objIndex)
{
return ::lua_getmetatable(toState, objIndex);
}
static int lua_equal(IntPtr luaState, int index1, int index2)
{
return ::lua_equal(toState, index1, index2);
}
static void lua_pushvalue(IntPtr luaState, int index)
{
::lua_pushvalue(toState, index);
}
static void lua_replace(IntPtr luaState, int index)
{
::lua_replace(toState, index);
}
static int lua_gettop(IntPtr luaState)
{
return ::lua_gettop(toState);
}
static LuaTypes lua_type(IntPtr luaState, int index)
{
return (LuaTypes) ::lua_type(toState, index);
}
#undef lua_isnil
static bool lua_isnil(IntPtr luaState, int index)
{
return lua_type(luaState,index)==LuaTypes::LUA_TNIL;
}
static bool lua_isnumber(IntPtr luaState, int index)
{
return lua_type(luaState,index)==LuaTypes::LUA_TNUMBER;
}
#undef lua_isboolean
static bool lua_isboolean(IntPtr luaState, int index)
{
return lua_type(luaState,index)==LuaTypes::LUA_TBOOLEAN;
}
static int luaL_ref(IntPtr luaState, int registryIndex)
{
return ::luaL_ref(toState, registryIndex);
}
#undef lua_ref
static int lua_ref(IntPtr luaState, int lockRef)
{
if(lockRef!=0)
{
return luaL_ref(luaState, (int) LuaIndexes::LUA_REGISTRYINDEX);
}
else return 0;
}
static void lua_rawgeti(IntPtr luaState, int tableIndex, int index)
{
::lua_rawgeti(toState, tableIndex, index);
}
static void lua_rawseti(IntPtr luaState, int tableIndex, int index)
{
::lua_rawseti(toState, tableIndex, index);
}
static IntPtr lua_newuserdata(IntPtr luaState, int size)
{
return IntPtr(::lua_newuserdata(toState, size));
}
static IntPtr lua_touserdata(IntPtr luaState, int index)
{
return IntPtr(::lua_touserdata(toState, index));
}
#undef lua_getref
static void lua_getref(IntPtr luaState, int reference)
{
lua_rawgeti(luaState, (int) LuaIndexes::LUA_REGISTRYINDEX,reference);
}
// Unwrapped
// static void luaL_unref(IntPtr luaState, int registryIndex, int reference);
#undef lua_unref
static void lua_unref(IntPtr luaState, int reference)
{
::luaL_unref(toState, (int) LuaIndexes::LUA_REGISTRYINDEX,reference);
}
static bool lua_isstring(IntPtr luaState, int index)
{
return ::lua_isstring(toState, index) != 0;
}
static bool lua_iscfunction(IntPtr luaState, int index)
{
return ::lua_iscfunction(toState, index) != 0;
}
static void lua_pushnil(IntPtr luaState)
{
::lua_pushnil(toState);
}
static void lua_call(IntPtr luaState, int nArgs, int nResults)
{
::lua_call(toState, nArgs, nResults);
}
static int lua_pcall(IntPtr luaState, int nArgs, int nResults, int errfunc)
{
return ::lua_pcall(toState, nArgs, nResults, errfunc);
}
// static int lua_rawcall(IntPtr luaState, int nArgs, int nResults)
static IntPtr lua_tocfunction(IntPtr luaState, int index)
{
return IntPtr(::lua_tocfunction(toState, index));
}
static double lua_tonumber(IntPtr luaState, int index)
{
return ::lua_tonumber(toState, index);
}
static bool lua_toboolean(IntPtr luaState, int index)
{
return ::lua_toboolean(toState, index);
}
// unwrapped
// was out strLen
// static IntPtr lua_tolstring(IntPtr luaState, int index, [Out] int ^ strLen);
#undef lua_tostring
static String^ lua_tostring(IntPtr luaState, int index)
{
#if 1
// FIXME use the same format string as lua i.e. LUA_NUMBER_FMT
LuaTypes t = lua_type(luaState,index);
if(t == LuaTypes::LUA_TNUMBER)
return String::Format("{0}", lua_tonumber(luaState, index));
else if(t == LuaTypes::LUA_TSTRING)
{
size_t strlen;
const char *str = ::lua_tolstring(toState, index, &strlen);
return Marshal::PtrToStringAnsi(IntPtr((char *) str), strlen);
}
else if(t == LuaTypes::LUA_TNIL)
return nullptr; // treat lua nulls to as C# nulls
else
return gcnew String("0"); // Because luaV_tostring does this
#else
size_t strlen;
// Note! This method will _change_ the representation of the object on the stack to a string.
// We do not want this behavior so we do the conversion ourselves
const char *str = ::lua_tolstring(toState, index, &strlen);
if (str)
return Marshal::PtrToStringAnsi(IntPtr((char *) str), strlen);
else
return nullptr; // treat lua nulls to as C# nulls
#endif
}
static void lua_atpanic(IntPtr luaState, LuaCSFunction^ panicf)
{
IntPtr p = Marshal::GetFunctionPointerForDelegate(panicf);
::lua_atpanic(toState, (lua_CFunction) p.ToPointer());
}
#if 0
// no longer needed - all our functions are now stdcall calling convention
static int stdcall_closure(lua_State *L) {
lua_CFunction fn = (lua_CFunction)lua_touserdata(L, lua_upvalueindex(1));
return fn(L);
}
#endif
static void lua_pushstdcallcfunction(IntPtr luaState, LuaCSFunction^ function)
{
IntPtr p = Marshal::GetFunctionPointerForDelegate(function);
lua_pushcfunction(toState, (lua_CFunction) p.ToPointer());
}
#if 0
// not yet implemented
static void lua_atlock(IntPtr luaState, LuaCSFunction^ lockf)
{
IntPtr p = Marshal::GetFunctionPointerForDelegate(lockf);
::lua_atlock(toState, (lua_CFunction) p.ToPointer());
}
static void lua_atunlock(IntPtr luaState, LuaCSFunction^ unlockf);
#endif
static void lua_pushnumber(IntPtr luaState, double number)
{
::lua_pushnumber(toState, number);
}
static void lua_pushboolean(IntPtr luaState, bool value)
{
::lua_pushboolean(toState, value);
}
#if 0
// Not yet wrapped
static void lua_pushlstring(IntPtr luaState, String^ str, int size)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(str).ToPointer();
//
Marshal::FreeHGlobal(IntPtr(cs));
}
#endif
static void lua_pushstring(IntPtr luaState, String^ str)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(str).ToPointer();
::lua_pushstring(toState, cs);
Marshal::FreeHGlobal(IntPtr(cs));
}
static int luaL_newmetatable(IntPtr luaState, String^ meta)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(meta).ToPointer();
int result = ::luaL_newmetatable(toState, cs);
Marshal::FreeHGlobal(IntPtr(cs));
return result;
}
// steffenj: BEGIN Lua 5.1.1 API change (luaL_getmetatable is now a macro using lua_getfield)
static void lua_getfield(IntPtr luaState, int stackPos, String^ meta)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(meta).ToPointer();
::lua_getfield(toState, stackPos, cs);
Marshal::FreeHGlobal(IntPtr(cs));
}
#undef luaL_getmetatable
static void luaL_getmetatable(IntPtr luaState, String^ meta)
{
lua_getfield(luaState, (int) LuaIndexes::LUA_REGISTRYINDEX, meta);
}
static IntPtr luaL_checkudata(IntPtr luaState, int stackPos, String^ meta)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(meta).ToPointer();
void *result = ::luaL_checkudata(toState, stackPos, cs);
Marshal::FreeHGlobal(IntPtr(cs));
return IntPtr(result);
}
static bool luaL_getmetafield(IntPtr luaState, int stackPos, String^ field)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(field).ToPointer();
int result = ::luaL_getmetafield(toState, stackPos, cs);
Marshal::FreeHGlobal(IntPtr(cs));
return result != 0;
}
// wrapper not yet implemented
// static int lua_load(IntPtr luaState, LuaChunkReader chunkReader, ref ReaderInfo data, String^ chunkName);
static int luaL_loadbuffer(IntPtr luaState, String^ buff, String^ name)
{
char *cs1 = (char *) Marshal::StringToHGlobalAnsi(buff).ToPointer();
char *cs2 = (char *) Marshal::StringToHGlobalAnsi(name).ToPointer();
//CP: fix for MBCS, changed to use cs1's length (reported by qingrui.li)
int result = ::luaL_loadbuffer(toState, cs1, strlen(cs1), cs2);
Marshal::FreeHGlobal(IntPtr(cs1));
Marshal::FreeHGlobal(IntPtr(cs2));
return result;
}
static int luaL_loadfile(IntPtr luaState, String^ filename)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(filename).ToPointer();
int result = ::luaL_loadfile(toState, cs);
Marshal::FreeHGlobal(IntPtr(cs));
return result;
}
static void lua_error(IntPtr luaState)
{
::lua_error(toState);
}
static bool lua_checkstack(IntPtr luaState,int extra)
{
return ::lua_checkstack(toState, extra) != 0;
}
static int lua_next(IntPtr luaState,int index)
{
return ::lua_next(toState, index);
}
static void lua_pushlightuserdata(IntPtr luaState, IntPtr udata)
{
::lua_pushlightuserdata(toState, udata.ToPointer());
}
static int luanet_rawnetobj(IntPtr luaState,int obj)
{
int *udata= (int *) lua_touserdata(luaState, obj).ToPointer();
if(udata!=NULL) return *udata;
return -1;
}
// lua debug hook functions added by Reinhard Ostermeier
static int lua_sethook(IntPtr luaState, LuaHookFunction^ func, int mask, int count)
{
IntPtr p;
if (func == nullptr)
{
p = IntPtr::Zero;
}
else
{
p = Marshal::GetFunctionPointerForDelegate(func);
}
return ::lua_sethook(toState, (lua_Hook)p.ToPointer(), mask, count);
}
static int lua_gethookmask(IntPtr luaState)
{
return ::lua_gethookmask(toState);
}
static int lua_gethookcount(IntPtr luaState)
{
return ::lua_gethookcount(toState);
}
static int lua_getstack(IntPtr luaState, int level, IntPtr luaDebug)
{
return ::lua_getstack(toState, level, (lua_Debug*)luaDebug.ToPointer());
}
static int lua_getinfo(IntPtr luaState, String^ what, IntPtr luaDebug)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(what).ToPointer();
int ret = ::lua_getinfo(toState, cs, (lua_Debug*)luaDebug.ToPointer());
Marshal::FreeHGlobal(IntPtr(cs));
return ret;
}
static String^ lua_getlocal(IntPtr luaState, IntPtr luaDebug, int n)
{
const char* str = ::lua_getlocal(toState, (lua_Debug*)luaDebug.ToPointer(), n);
if (str == NULL)
{
return nullptr;
}
else
{
return gcnew String(str);
}
}
static String^ lua_setlocal(IntPtr luaState, IntPtr luaDebug, int n)
{
const char* str = ::lua_setlocal(toState, (lua_Debug*)luaDebug.ToPointer(), n);
if(str == NULL)
{
return nullptr;
}
else
{
return gcnew String(str);
}
}
static String^ lua_getupvalue(IntPtr luaState, int funcindex, int n)
{
const char* str = ::lua_getupvalue(toState, funcindex, n);
if(str == NULL)
{
return nullptr;
}
else
{
return gcnew String(str);
}
}
static String^ lua_setupvalue(IntPtr luaState, int funcindex, int n)
{
const char* str = ::lua_setupvalue(toState, funcindex, n);
if(str == NULL)
{
return nullptr;
}
else
{
return gcnew String(str);
}
}
// end of lua debug hook functions
private:
// Starting with 5.1 the auxlib version of checkudata throws an exception if the type isn't right
// Instead, we want to run our own version that checks the type and just returns null for failure
static void *checkudata_raw(lua_State *L, int ud, const char *tname)
{
void *p = ::lua_touserdata(L, ud);
if (p != NULL)
{ /* value is a userdata? */
if (::lua_getmetatable(L, ud))
{
int isEqual;
/* does it have a metatable? */
::lua_getfield(L, (int) LuaIndexes::LUA_REGISTRYINDEX, tname); /* get correct metatable */
isEqual = lua_rawequal(L, -1, -2);
// NASTY - we need our own version of the lua_pop macro
// lua_pop(L, 2); /* remove both metatables */
::lua_settop(L, -(2) - 1);
if (isEqual) /* does it have the correct mt? */
return p;
}
}
return NULL;
}
public:
static int luanet_checkudata(IntPtr luaState, int ud, String ^tname)
{
char *cs = (char *) Marshal::StringToHGlobalAnsi(tname).ToPointer();
int *udata=(int*) checkudata_raw(toState, ud, cs);
Marshal::FreeHGlobal(IntPtr(cs));
if(udata!=NULL) return *udata;
return -1;
}
static bool luaL_checkmetatable(IntPtr luaState,int index)
{
int retVal=0;
if(lua_getmetatable(luaState,index)!=0)
{
lua_pushlightuserdata(luaState, IntPtr(&tag));
lua_rawget(luaState, -2);
retVal = !lua_isnil(luaState, -1);
lua_settop(luaState, -3);
}
return retVal;
}
static IntPtr luanet_gettag()
{
return IntPtr(&tag);
}
static void luanet_newudata(IntPtr luaState,int val)
{
int* pointer=(int*) lua_newuserdata(luaState, sizeof(int)).ToPointer();
*pointer=val;
}
static int luanet_tonetobject(IntPtr luaState,int index)
{
int *udata;
if(lua_type(luaState,index)==LuaTypes::LUA_TUSERDATA)
{
if(luaL_checkmetatable(luaState, index))
{
udata=(int*) lua_touserdata(luaState,index).ToPointer();
if(udata!=NULL)
return *udata;
}
udata=(int*)checkudata_raw(toState,index, "luaNet_class");
if(udata!=NULL) return *udata;
udata=(int*)checkudata_raw(toState,index, "luaNet_searchbase");
if(udata!=NULL) return *udata;
udata=(int*)checkudata_raw(toState,index, "luaNet_function");
if(udata!=NULL) return *udata;
}
return -1;
}
#if 0
[DllImport(STUBDLL,CallingConvention=CallingConvention.Cdecl)]
#endif
};
}