584 lines
13 KiB
C++
584 lines
13 KiB
C++
|
|
#include <cstdarg>
|
|
#include <algorithm>
|
|
|
|
#include <toolkit/XConsole.h>
|
|
#include <toolkit/XSTLUtil.h>
|
|
#include <toolkit/XENV.h>
|
|
#include <cipher/XEncrypt.h>
|
|
#include <logging/FileLog.h>
|
|
|
|
#include "LuaVM.h"
|
|
|
|
|
|
static XCriticalSection s_CS( "LuaVMLock" );
|
|
|
|
LuaVM::LUA_INFO* g_pGlobalLuaLocalInfo = NULL;
|
|
__declspec( thread ) LuaVM::LUA_INFO* g_pLuaLocalInfo = NULL;
|
|
|
|
const char * LuaVM::LUA_FILE_FORMAT_TAG = "LA";
|
|
std::vector< struct LuaVM::LUA_INFO * > LuaVM::m_vStateList;
|
|
std::map< std::string, luaFunction > LuaVM::m_mapFuncList;
|
|
std::vector< LuaVM::LuaScriptInfo > LuaVM::m_vScriptList;
|
|
|
|
|
|
static void enum_lua_stack(lua_State *L)
|
|
{
|
|
int top = lua_gettop(L);
|
|
_cprint( "Stack: %d", top);
|
|
for( int i = 1; i <= lua_gettop(L); ++i )
|
|
{
|
|
switch( lua_type( L, i ) )
|
|
{
|
|
case LUA_TNIL:
|
|
_cprint( " %s", lua_typename(L, lua_type(L, i)));
|
|
break;
|
|
case LUA_TBOOLEAN:
|
|
_cprint( " %s: %s", lua_typename(L, lua_type(L, i)), lua_toboolean(L, i)?"true":"false");
|
|
break;
|
|
case LUA_TLIGHTUSERDATA:
|
|
_cprint(" %s: 0x%p", lua_typename(L, lua_type(L, i)), lua_topointer(L, i));
|
|
break;
|
|
case LUA_TNUMBER:
|
|
_cprint(" %s: %f", lua_typename(L, lua_type(L, i)), lua_tonumber(L, i));
|
|
break;
|
|
case LUA_TSTRING:
|
|
_cprint(" %s: %s", lua_typename(L, lua_type(L, i)), lua_tostring(L, i));
|
|
break;
|
|
case LUA_TTABLE:
|
|
_cprint(" %s: 0x%p", lua_typename(L, lua_type(L, i)), lua_topointer(L, i));
|
|
break;
|
|
case LUA_TFUNCTION:
|
|
_cprint(" %s(): 0x%p", lua_typename(L, lua_type(L, i)), lua_topointer(L, i));
|
|
break;
|
|
case LUA_TUSERDATA:
|
|
_cprint(" %s: 0x%p", lua_typename(L, lua_type(L, i)), lua_topointer(L, i));
|
|
break;
|
|
case LUA_TTHREAD:
|
|
_cprint(" %s", lua_typename(L, lua_type(L, i)));
|
|
break;
|
|
}
|
|
}
|
|
|
|
_cprint("\n");
|
|
}
|
|
|
|
static void lua_call_stack(lua_State* L, int n=0)
|
|
{
|
|
lua_Debug ar;
|
|
if(lua_getstack(L, n, &ar) == 1)
|
|
{
|
|
lua_getinfo(L, "nSlu", &ar);
|
|
|
|
if(n == 0)
|
|
{
|
|
_cprint("\t<call stack>\n");
|
|
}
|
|
|
|
if(ar.name)
|
|
_cprint(" %s() : line %d [%s : line %d]\n", ar.name, ar.currentline, ar.source, ar.linedefined);
|
|
else
|
|
_cprint(" unknown : line %d [%s : line %d]\n", ar.currentline, ar.source, ar.linedefined);
|
|
|
|
lua_call_stack(L, n+1);
|
|
}
|
|
}
|
|
|
|
static int lua_on_error(lua_State *L)
|
|
{
|
|
_cprint("error: %s\n", lua_tostring(L, -1));
|
|
lua_call_stack(L, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int _outputConsole(lua_State *L)
|
|
{
|
|
int n = lua_gettop(L); /* number of arguments */
|
|
for (int i = 1; i <= n; i++)
|
|
{
|
|
if( lua_isstring(L, i) )
|
|
{
|
|
LUA()->Log( lua_tostring( L, i ) );
|
|
continue;
|
|
}
|
|
|
|
if( lua_isnumber(L, i) )
|
|
{
|
|
char szTmp[30];
|
|
s_sprintf( szTmp, _countof( szTmp ), "%f", lua_tonumber( L, i ) );
|
|
LUA()->Log( szTmp );
|
|
continue;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
static int _lua_alert(lua_State *L)
|
|
{
|
|
_outputConsole( L );
|
|
return 0;
|
|
}*/
|
|
|
|
struct _LOG_FUNC
|
|
{
|
|
void (*fp)( const char* );
|
|
};
|
|
|
|
LuaVM::LuaVM( bool bGlobalInstance )
|
|
: m_bIsGlobalInstance( bGlobalInstance )
|
|
{
|
|
m_pLogFunc = new _LOG_FUNC;
|
|
|
|
m_pLogFunc->fp = NULL;
|
|
|
|
m_bInit = false;
|
|
}
|
|
|
|
LuaVM::~LuaVM()
|
|
{
|
|
delete m_pLogFunc;
|
|
}
|
|
|
|
bool LuaVM::Init()
|
|
{
|
|
THREAD_SYNCRONIZE( s_CS );
|
|
|
|
if( m_bInit )
|
|
return false;
|
|
|
|
GlobalInst();
|
|
Inst();
|
|
|
|
m_bInit = true;
|
|
return true;
|
|
}
|
|
|
|
bool LuaVM::DeInit()
|
|
{
|
|
THREAD_SYNCRONIZE( s_CS );
|
|
|
|
std::vector< struct LUA_INFO * >::iterator it;
|
|
|
|
for( it = m_vStateList.begin(); it != m_vStateList.end(); ++it )
|
|
{
|
|
{
|
|
THREAD_SYNCRONIZE( (*it)->cs );
|
|
lua_close( (*it)->pState );
|
|
}
|
|
|
|
delete (*it);
|
|
}
|
|
|
|
m_vStateList.clear();
|
|
|
|
return true;
|
|
}
|
|
|
|
void LuaVM::reload( lua_State* pState )
|
|
{
|
|
{ // 함수들 등록
|
|
std::map< std::string, luaFunction >::iterator it;
|
|
|
|
for( it = m_mapFuncList.begin(); it != m_mapFuncList.end(); ++it )
|
|
{
|
|
lua_register( pState, it->first.c_str(), it->second );
|
|
}
|
|
}
|
|
|
|
{ // 스크립트 파일 로딩
|
|
std::vector< LuaScriptInfo >::iterator it;
|
|
|
|
for( it = m_vScriptList.begin(); it != m_vScriptList.end(); ++it )
|
|
{
|
|
doFile( pState, (*it).m_strFilename.c_str(), (*it).m_bEncrypted );
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool LuaVM::doFile( lua_State * pState, const char * pszFilename, const bool bEncrypted, const unsigned int nStrictFileVersion )
|
|
{
|
|
FILE * fp = NULL;
|
|
if( fopen_s( &fp, pszFilename, "rb" ) )
|
|
return false;
|
|
|
|
struct FileCloser
|
|
{
|
|
FileCloser( FILE * _fp ) : fp( _fp ) {}
|
|
~FileCloser() { fclose( fp ); }
|
|
|
|
FILE * fp;
|
|
} fc( fp );
|
|
|
|
size_t nContentsLength = 0;
|
|
|
|
fseek( fp, 0, SEEK_END );
|
|
nContentsLength = ftell( fp );
|
|
fseek( fp, 0, SEEK_SET );
|
|
|
|
// No header
|
|
if( !nContentsLength )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
BYTE * pBuffer = new BYTE[ nContentsLength+1 ];
|
|
struct MemoryDeallocator
|
|
{
|
|
MemoryDeallocator( BYTE * _pBuffer ) : pBuffer( _pBuffer ) {}
|
|
~MemoryDeallocator() { delete [] pBuffer; }
|
|
|
|
BYTE * pBuffer;
|
|
} md( pBuffer );
|
|
|
|
size_t nReadNum = fread( pBuffer, sizeof( BYTE ), nContentsLength, fp );
|
|
if( nReadNum != nContentsLength )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
pBuffer[nContentsLength] = 0;
|
|
const char* pScript = reinterpret_cast< const char* >( pBuffer );
|
|
std::vector< BYTE > vDecrypted;
|
|
if( bEncrypted )
|
|
{
|
|
const unsigned int nVersion = XEncrypt::GetFormatVersion( LUA_FILE_FORMAT_TAG, pBuffer, nContentsLength );
|
|
if( nVersion == XEncrypt::INVALID_VERSION || nStrictFileVersion && nVersion != nStrictFileVersion )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
switch( nVersion )
|
|
{
|
|
case LUA_FILE_VERSION:
|
|
if( !XEncrypt::Decrypt( XEncrypt::ET_ZLIB64, LUA_FILE_FORMAT_TAG, LUA_FILE_VERSION, pBuffer, nContentsLength, vDecrypted ) )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
break;
|
|
case LUA_FILE_VERSION2:
|
|
if( !XEncrypt::Decrypt( XEncrypt::ET_ZLIB_WITH_SIMPLE_CIPHER, LUA_FILE_FORMAT_TAG, LUA_FILE_VERSION2, pBuffer, nContentsLength, vDecrypted ) )
|
|
{
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
return false;
|
|
}
|
|
|
|
if( vDecrypted.empty() )
|
|
return true;
|
|
|
|
vDecrypted.push_back( NULL );
|
|
nContentsLength = vDecrypted.size();
|
|
pScript = reinterpret_cast< char * >( &vDecrypted.front() );
|
|
}
|
|
|
|
const BYTE* pBOMCheck = reinterpret_cast< const BYTE* >( pScript );
|
|
if( nContentsLength > 3 && pBOMCheck[0] == 0xEF && pBOMCheck[1] == 0xBB && pBOMCheck[2] == 0xBF )
|
|
{
|
|
pScript = &pScript[3];
|
|
}
|
|
|
|
int pre_idx = lua_gettop( pState );
|
|
if( luaL_dostring( pState, pScript ) != 0 )
|
|
{
|
|
const char* err_msg = lua_tostring( pState, -1 );
|
|
FILELOG( "Lua run doFile failed. %s(%s)", pszFilename, err_msg );
|
|
_cprint( "Lua run doFile failed. %s(%s)\n", pszFilename, err_msg );
|
|
|
|
lua_pop( pState, 1 );
|
|
return false;
|
|
}
|
|
|
|
lua_settop( pState, pre_idx ); // 스텍 원상복구
|
|
return true;
|
|
}
|
|
|
|
bool LuaVM::Reload()
|
|
{
|
|
THREAD_SYNCRONIZE( s_CS );
|
|
|
|
for( std::vector< struct LUA_INFO * >::iterator it = m_vStateList.begin(); it != m_vStateList.end(); ++it )
|
|
{
|
|
THREAD_SYNCHRONIZE1( (*it)->cs );
|
|
|
|
reload( (*it)->pState );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LuaVM::InitThread( LuaVM::LUA_INFO** pLuaLocalInfo )
|
|
{
|
|
if( *pLuaLocalInfo ) return false;
|
|
|
|
LUA_INFO * pLuaInfo = new LUA_INFO;
|
|
|
|
pLuaInfo->pState = luaL_newstate();
|
|
|
|
if( !pLuaInfo->pState )
|
|
{
|
|
assert( 0 && "lua_open() FAILED!!!" );
|
|
delete pLuaInfo;
|
|
return false;
|
|
}
|
|
|
|
luaL_openlibs( pLuaInfo->pState );
|
|
|
|
lua_register( pLuaInfo->pState, "_ALERT", _outputConsole );
|
|
lua_register( pLuaInfo->pState, "cprint", _outputConsole );
|
|
|
|
|
|
THREAD_SYNCRONIZE( s_CS );
|
|
|
|
reload( pLuaInfo->pState );
|
|
lua_settop( pLuaInfo->pState, 0 );
|
|
|
|
m_vStateList.push_back( pLuaInfo );
|
|
*pLuaLocalInfo = pLuaInfo;
|
|
return true;
|
|
}
|
|
|
|
bool LuaVM::DeInitThread()
|
|
{
|
|
if( !g_pLuaLocalInfo ) return false;
|
|
|
|
{
|
|
THREAD_SYNCRONIZE( s_CS );
|
|
|
|
std::vector< struct LUA_INFO * >::iterator it = std::find( m_vStateList.begin(), m_vStateList.end(), g_pLuaLocalInfo );
|
|
|
|
if( it != m_vStateList.end() )
|
|
{
|
|
vector_fast_erase( &m_vStateList, it );
|
|
}
|
|
}
|
|
|
|
{
|
|
THREAD_SYNCRONIZE( g_pLuaLocalInfo->cs );
|
|
lua_close( g_pLuaLocalInfo->pState );
|
|
}
|
|
|
|
delete g_pLuaLocalInfo;
|
|
g_pLuaLocalInfo = NULL;
|
|
|
|
return true;
|
|
}
|
|
|
|
void LuaVM::BindLogOutputFunction( void (*fp)( const char* ) )
|
|
{
|
|
m_pLogFunc->fp = fp;
|
|
}
|
|
|
|
void LuaVM::Log( const char *szString )
|
|
{
|
|
if( m_pLogFunc->fp )
|
|
{
|
|
m_pLogFunc->fp( szString );
|
|
m_pLogFunc->fp( "\n" );
|
|
}
|
|
else
|
|
{
|
|
#ifndef _STOOL
|
|
#ifndef _RAC
|
|
// FILELOG( "%s", szString );
|
|
_cprint( "%s\n", szString );
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void LuaVM::LogPrintf( const char *szString, ... )
|
|
{
|
|
char szBuf[2048];
|
|
va_list va;
|
|
va_start( va, szString );
|
|
s_vsprintf( szBuf, _countof(szBuf), szString, va );
|
|
va_end( va );
|
|
|
|
this->Log( szBuf );
|
|
}
|
|
|
|
void LuaVM::ResetLoadedScriptList()
|
|
{
|
|
THREAD_SYNCRONIZE( s_CS );
|
|
m_vScriptList.clear();
|
|
}
|
|
|
|
const bool LuaVM::LoadScript( const char* szFileName, const bool bEncrypted )
|
|
{
|
|
THREAD_SYNCRONIZE( s_CS );
|
|
|
|
bool bResult = true;
|
|
|
|
for( std::vector< struct LUA_INFO * >::iterator it = m_vStateList.begin(); it != m_vStateList.end(); ++it )
|
|
{
|
|
THREAD_SYNCRONIZE( (*it)->cs );
|
|
|
|
if( doFile( (*it)->pState, szFileName, bEncrypted ) == false )
|
|
{
|
|
bResult = false;
|
|
}
|
|
}
|
|
|
|
LuaScriptInfo scriptInfo( szFileName, bEncrypted );
|
|
|
|
std::vector< LuaScriptInfo >::iterator itScript = std::find( m_vScriptList.begin(), m_vScriptList.end(), scriptInfo );
|
|
if( itScript == m_vScriptList.end() )
|
|
m_vScriptList.push_back( scriptInfo );
|
|
else
|
|
(*itScript).m_bEncrypted = bEncrypted;
|
|
|
|
return bResult;
|
|
}
|
|
|
|
bool LuaVM::RunString( const char* szString, std::string * pstrReturnValue )
|
|
{
|
|
LUA_INFO *pLuaInfo = getLuaInfo();
|
|
|
|
if( !pLuaInfo ) return false;
|
|
|
|
THREAD_SYNCRONIZE( pLuaInfo->cs );
|
|
|
|
int pre_idx = lua_gettop( pLuaInfo->pState );
|
|
int code_page = ENV().GetInt( "CodePage", CP_ACP );
|
|
if( code_page >= 0 )
|
|
{
|
|
assert( strlen( szString ) < 1024 );
|
|
|
|
wchar_t wszString[1024];
|
|
MultiByteToWideChar( code_page, 0, szString, -1, wszString, _countof( wszString ) );
|
|
|
|
char szUTFString[3*1024];
|
|
WideCharToMultiByte( CP_UTF8, 0, wszString, -1, szUTFString, _countof( szUTFString ), NULL, NULL );
|
|
|
|
if( luaL_dostring( pLuaInfo->pState, szUTFString ) != 0 )
|
|
{
|
|
const char* err_msg = lua_tostring( pLuaInfo->pState, -1 );
|
|
FILELOG( "Lua run string failed. %s(%s)", szUTFString, err_msg );
|
|
_cprint( "Lua run string failed. %s(%s)\n", szUTFString, err_msg );
|
|
|
|
lua_pop( pLuaInfo->pState, 1 );
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( luaL_dostring( pLuaInfo->pState, szString ) != 0 )
|
|
{
|
|
const char* err_msg = lua_tostring( pLuaInfo->pState, -1 );
|
|
FILELOG( "Lua run string failed. %s(%s)", szString, err_msg );
|
|
_cprint( "Lua run string failed. %s(%s)\n", szString, err_msg );
|
|
|
|
lua_pop( pLuaInfo->pState, 1 );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int ret_count = lua_gettop( pLuaInfo->pState );
|
|
if( pstrReturnValue && pre_idx < ret_count ) // 리턴값이 있다면
|
|
{
|
|
int type = ::lua_type( pLuaInfo->pState, -1 );
|
|
if( type == LUA_TBOOLEAN )
|
|
{
|
|
if( lua_toboolean( pLuaInfo->pState, -1 ) )
|
|
*pstrReturnValue = "true";
|
|
else
|
|
*pstrReturnValue = "false";
|
|
}
|
|
else if( type == LUA_TNIL )
|
|
{
|
|
*pstrReturnValue = "nil";
|
|
}
|
|
else
|
|
{
|
|
*pstrReturnValue = lua_tostring_utf8( pLuaInfo->pState, -1 );
|
|
}
|
|
}
|
|
|
|
lua_settop( pLuaInfo->pState, pre_idx ); // 스텍 원상복구
|
|
return true;
|
|
}
|
|
|
|
bool LuaVM::RegisterFunction( const char* szFunctionName, luaFunction fp )
|
|
{
|
|
THREAD_SYNCRONIZE( s_CS );
|
|
|
|
std::vector< struct LUA_INFO * >::iterator it;
|
|
|
|
for( it = m_vStateList.begin(); it != m_vStateList.end(); ++it )
|
|
{
|
|
THREAD_SYNCRONIZE( (*it)->cs );
|
|
|
|
lua_register( (*it)->pState, szFunctionName, fp );
|
|
}
|
|
|
|
m_mapFuncList[szFunctionName] = fp;
|
|
|
|
return true;
|
|
}
|
|
|
|
LuaVM::LUA_INFO * LuaVM::getLuaInfo()
|
|
{
|
|
if( m_bIsGlobalInstance )
|
|
{
|
|
if( !g_pGlobalLuaLocalInfo ) InitThread( &g_pGlobalLuaLocalInfo );
|
|
return g_pGlobalLuaLocalInfo;
|
|
}
|
|
|
|
if( !g_pLuaLocalInfo ) InitThread( &g_pLuaLocalInfo );
|
|
return g_pLuaLocalInfo;
|
|
}
|
|
|
|
LuaVM* LuaVM::GlobalInst()
|
|
{
|
|
static LuaVM inst( true );
|
|
return &inst;
|
|
}
|
|
|
|
LuaVM* LuaVM::Inst()
|
|
{
|
|
static LuaVM inst;
|
|
return &inst;
|
|
}
|
|
|
|
void lua_pushstring_utf8( lua_State * L, const char * s )
|
|
{
|
|
assert( strlen( s ) < 1024 );
|
|
|
|
wchar_t wszString[1024];
|
|
MultiByteToWideChar( ENV().GetInt( "CodePage", CP_ACP ), 0, s, -1, wszString, _countof( wszString ) );
|
|
|
|
char szUTFString[3*1024];
|
|
WideCharToMultiByte( CP_UTF8, 0, wszString, -1, szUTFString, _countof( szUTFString ), NULL, NULL );
|
|
|
|
lua_pushstring( L, szUTFString );
|
|
}
|
|
|
|
std::string lua_tostring_utf8( lua_State *L, int idx )
|
|
{
|
|
// 문자열로 인식할 수 없는 파라미터(따옴표 빼먹는게 주 원인 -_ -)를 가져오려고 했다면 빈 문자열 반환
|
|
if( !lua_isstring( L, idx ) ) return "";
|
|
|
|
const char * s = lua_tostring( L, idx );
|
|
|
|
assert( strlen( s ) < 1024 );
|
|
|
|
wchar_t wszString[1024];
|
|
if( MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, wszString, _countof( wszString ) ) <= 0 )
|
|
{
|
|
return s;
|
|
}
|
|
|
|
char szMBCSString[3*1024];
|
|
WideCharToMultiByte( ENV().GetInt( "CodePage", CP_ACP ), 0, wszString, -1, szMBCSString, _countof( szMBCSString ), NULL, NULL );
|
|
|
|
return szMBCSString;
|
|
} |