diff --git a/src/LuaBinding.cpp b/src/LuaBinding.cpp index 355c6487f9..15f82d8842 100644 --- a/src/LuaBinding.cpp +++ b/src/LuaBinding.cpp @@ -2,7 +2,6 @@ #include "LuaBinding.h" #include "LuaReference.h" #include "RageUtil.h" -#include "Foreach.h" #include "SubscriptionManager.h" static SubscriptionManager m_Subscribers; @@ -11,15 +10,17 @@ namespace { void RegisterTypes( lua_State *L ) { - if( m_Subscribers.m_pSubscribers == NULL ) + if( m_Subscribers.m_pSubscribers == nullptr ) return; /* Register base classes first. */ - map mapToRegister; - FOREACHS( LuaBinding*, *m_Subscribers.m_pSubscribers, p ) - mapToRegister[(*p)->GetClassName()] = (*p); + std::map mapToRegister; + for (auto *p: *m_Subscribers.m_pSubscribers) + { + mapToRegister[p->GetClassName()] = p; + } - set setRegisteredAlready; + std::set setRegisteredAlready; while( !mapToRegister.empty() ) { @@ -32,8 +33,8 @@ namespace { break; } - RString sBase = pBinding->GetBaseClassName(); - map::const_iterator it = mapToRegister.find(sBase); + std::string sBase = pBinding->GetBaseClassName(); + auto it = mapToRegister.find(sBase); if( it != mapToRegister.end() ) { pBinding = it->second; @@ -75,7 +76,7 @@ void LuaBinding::Register( lua_State *L ) int methods = lua_gettop( L ); /* Create a metatable for the userdata objects. */ - luaL_newmetatable( L, GetClassName() ); + luaL_newmetatable( L, GetClassName().c_str() ); int metatable = lua_gettop( L ); // We use the metatable to determine the type of the table, so don't @@ -101,17 +102,17 @@ void LuaBinding::Register( lua_State *L ) // to the base class. if( IsDerivedClass() ) { - lua_getfield( L, LUA_GLOBALSINDEX, GetBaseClassName() ); + lua_getfield( L, LUA_GLOBALSINDEX, GetBaseClassName().c_str() ); lua_setfield( L, methods_metatable, "__index" ); - lua_pushstring( L, GetBaseClassName() ); + lua_pushstring( L, GetBaseClassName().c_str() ); lua_setfield( L, metatable, "base" ); } - lua_pushstring( L, GetClassName() ); + lua_pushstring( L, GetClassName().c_str() ); lua_setfield( L, methods_metatable, "class" ); - lua_pushstring( L, GetClassName() ); + lua_pushstring( L, GetClassName().c_str() ); LuaHelpers::PushValueFunc( L, 1 ); lua_setfield( L, metatable, "__type" ); // for luaL_pushtype @@ -119,16 +120,16 @@ void LuaBinding::Register( lua_State *L ) lua_newtable( L ); int iHeirarchyTable = lua_gettop( L ); - RString sClass = GetClassName(); + std::string sClass = GetClassName(); int iIndex = 0; while( !sClass.empty() ) { - lua_pushstring( L, sClass ); + lua_pushstring( L, sClass.c_str() ); lua_pushinteger( L, iIndex ); lua_rawset( L, iHeirarchyTable ); ++iIndex; - luaL_getmetatable( L, sClass ); + luaL_getmetatable( L, sClass.c_str() ); ASSERT( !lua_isnil(L, -1) ); lua_getfield( L, -1, "base" ); @@ -155,11 +156,11 @@ void LuaBinding::Register( lua_State *L ) // types (eg. "Actor.x(GAMESTATE, 10)"), which will crash or cause corruption. // #define FAST_LUA -void LuaBinding::CreateMethodsTable( lua_State *L, const RString &sName ) +void LuaBinding::CreateMethodsTable( lua_State *L, const std::string &sName ) { lua_newtable( L ); lua_pushvalue( L, -1 ); - lua_setfield( L, LUA_GLOBALSINDEX, sName ); + lua_setfield( L, LUA_GLOBALSINDEX, sName.c_str() ); } int LuaBinding::PushEqual( lua_State *L ) @@ -217,7 +218,7 @@ bool LuaBinding::Equal( lua_State *L ) * Get a userdata, and check that it's either szType or a type * derived from szType, by checking the heirarchy table. */ -bool LuaBinding::CheckLuaObjectType( lua_State *L, int iArg, const char *szType ) +bool LuaBinding::CheckLuaObjectType( lua_State *L, int iArg, std::string const &szType ) { #if defined(FAST_LUA) return true; @@ -233,7 +234,7 @@ bool LuaBinding::CheckLuaObjectType( lua_State *L, int iArg, const char *szType return false; } - lua_getfield( L, -1, szType ); + lua_getfield( L, -1, szType.c_str() ); bool bRet = !lua_isnil( L, -1 ); lua_pop( L, 2 ); @@ -255,7 +256,7 @@ static void GetGlobalTable( Lua *L ) /* The object is on the stack. It's either a table or a userdata. * If needed, associate the metatable; if a table, also add it to * the userdata table. */ -void LuaBinding::ApplyDerivedType( Lua *L, const RString &sClassName, void *pSelf ) +void LuaBinding::ApplyDerivedType( Lua *L, const std::string &sClassName, void *pSelf ) { int iTable = lua_gettop( L ); @@ -290,14 +291,14 @@ void LuaBinding::ApplyDerivedType( Lua *L, const RString &sClassName, void *pSel lua_settop( L, iTable ); } - luaL_getmetatable( L, sClassName ); + luaL_getmetatable( L, sClassName.c_str() ); lua_setmetatable( L, iTable ); } #include "RageUtil_AutoPtr.h" REGISTER_CLASS_TRAITS( LuaClass, new LuaClass(*pCopy) ) -void *LuaBinding::GetPointerFromStack( Lua *L, const RString &sType, int iArg ) +void *LuaBinding::GetPointerFromStack( Lua *L, const std::string &sType, int iArg ) { iArg = LuaHelpers::AbsIndex( L, iArg ); @@ -322,7 +323,7 @@ void *LuaBinding::GetPointerFromStack( Lua *L, const RString &sType, int iArg ) return *pData; } else - return NULL; + return nullptr; } /* Tricky: when an instance table is copied, we want to do a deep @@ -354,7 +355,7 @@ LuaClass &LuaClass::operator=( const LuaClass &cpy ) LuaClass::~LuaClass() { - if( LUA == NULL ) + if( LUA == nullptr ) return; Lua *L = LUA->Get(); @@ -394,7 +395,7 @@ float FArgGTEZero(lua_State* L, int index) /* * (c) 2005 Glenn Maynard * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -404,7 +405,7 @@ float FArgGTEZero(lua_State* L, int index) * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF diff --git a/src/LuaBinding.h b/src/LuaBinding.h index d5b170df57..19be6fbe54 100644 --- a/src/LuaBinding.h +++ b/src/LuaBinding.h @@ -12,21 +12,21 @@ class LuaBinding LuaBinding(); virtual ~LuaBinding(); void Register( lua_State *L ); - + static void RegisterTypes( lua_State *L ); bool IsDerivedClass() const { return GetClassName() != GetBaseClassName(); } - virtual const RString &GetClassName() const = 0; - virtual const RString &GetBaseClassName() const = 0; + virtual const std::string &GetClassName() const = 0; + virtual const std::string &GetBaseClassName() const = 0; - static void ApplyDerivedType( Lua *L, const RString &sClassname, void *pSelf ); - static bool CheckLuaObjectType( lua_State *L, int narg, const char *szType ); + static void ApplyDerivedType( Lua *L, const std::string &sClassname, void *pSelf ); + static bool CheckLuaObjectType( lua_State *L, int narg, std::string const &szType ); protected: virtual void Register( Lua *L, int iMethods, int iMetatable ) = 0; - static void CreateMethodsTable( lua_State *L, const RString &szName ); - static void *GetPointerFromStack( Lua *L, const RString &sType, int iArg ); + static void CreateMethodsTable( lua_State *L, const std::string &szName ); + static void *GetPointerFromStack( Lua *L, const std::string &sType, int iArg ); static bool Equal( lua_State *L ); static int PushEqual( lua_State *L ); @@ -42,7 +42,7 @@ class Luna: public LuaBinding struct RegType { - const char *szName; + std::string regName; binding_t *mfunc; }; @@ -52,20 +52,19 @@ class Luna: public LuaBinding lua_setfield( L, iMetatable, "__tostring" ); // fill method table with methods from class T - for( unsigned i=0; i < m_aMethods.size(); i++ ) + for (auto const m: m_aMethods) { - const RegType *l = &m_aMethods[i]; - lua_pushlightuserdata( L, (void*) l->mfunc ); + lua_pushlightuserdata( L, (void*) m.mfunc ); lua_pushcclosure( L, thunk, 1 ); - lua_setfield( L, iMethods, l->szName ); + lua_setfield( L, iMethods, m.regName.c_str() ); } } public: - virtual const RString &GetClassName() const { return m_sClassName; } - virtual const RString &GetBaseClassName() const { return m_sBaseClassName; } - static RString m_sClassName; - static RString m_sBaseClassName; + virtual const std::string &GetClassName() const { return m_sClassName; } + virtual const std::string &GetBaseClassName() const { return m_sBaseClassName; } + static std::string m_sClassName; + static std::string m_sBaseClassName; // Get userdata from the Lua stack and return a pointer to T object. static T *check( lua_State *L, int narg, bool bIsSelf = false ) @@ -73,27 +72,30 @@ class Luna: public LuaBinding if( !LuaBinding::CheckLuaObjectType(L, narg, m_sClassName) ) { if( bIsSelf ) - luaL_typerror( L, narg, m_sClassName ); + { + luaL_typerror( L, narg, m_sClassName.c_str()); + } else + { LuaHelpers::TypeError( L, narg, m_sClassName ); + } } - return get( L, narg ); } - + static T *get( lua_State *L, int narg ) { return (T *) GetPointerFromStack( L, m_sClassName, narg ); } - + /* Push a table or userdata for the given object. This is called on the - * base class, so we pick up the instance of the base class, if any. */ - static void PushObject( Lua *L, const RString &sDerivedClassName, T* p ); + * base class, so we pick up the instance of the base class, if any. */ + static void PushObject( Lua *L, const std::string &sDerivedClassName, T* p ); protected: - void AddMethod( const char *szName, int (*pFunc)(T *p, lua_State *L) ) + void AddMethod( std::string const ®Name, int (*pFunc)(T *p, lua_State *L) ) { - RegType r = { szName, pFunc }; + RegType r = { regName, pFunc }; m_aMethods.push_back(r); } @@ -109,7 +111,7 @@ class Luna: public LuaBinding return pFunc( obj, L ); // call member function } - vector m_aMethods; + std::vector m_aMethods; static int tostring_T( lua_State *L ) { @@ -139,24 +141,24 @@ class LuaClass: public LuaTable * Lua table). Derived classes simply call the base class's Push function, * specifying a different class name, so they don't need to know about it. */ #define LUA_REGISTER_INSTANCED_BASE_CLASS( T ) \ - template<> void Luna::PushObject( Lua *L, const RString &sDerivedClassName, T* p ) { p->m_pLuaInstance->PushSelf( L ); LuaBinding::ApplyDerivedType( L, sDerivedClassName, p ); } \ + template<> void Luna::PushObject( Lua *L, const std::string &sDerivedClassName, T* p ) { p->m_pLuaInstance->PushSelf( L ); LuaBinding::ApplyDerivedType( L, sDerivedClassName, p ); } \ LUA_REGISTER_CLASS_BASIC( T, T ) #define LUA_REGISTER_CLASS( T ) \ - template<> void Luna::PushObject( Lua *L, const RString &sDerivedClassName, T* p ) { void **pData = (void **) lua_newuserdata( L, sizeof(void *) ); *pData = p; LuaBinding::ApplyDerivedType( L, sDerivedClassName, p ); } \ + template<> void Luna::PushObject( Lua *L, const std::string &sDerivedClassName, T* p ) { void **pData = (void **) lua_newuserdata( L, sizeof(void *) ); *pData = p; LuaBinding::ApplyDerivedType( L, sDerivedClassName, p ); } \ LUA_REGISTER_CLASS_BASIC( T, T ) #define LUA_REGISTER_DERIVED_CLASS( T, B ) \ - template<> void Luna::PushObject( Lua *L, const RString &sDerivedClassName, T* p ) { Luna::PushObject( L, sDerivedClassName, p ); } \ + template<> void Luna::PushObject( Lua *L, const std::string &sDerivedClassName, T* p ) { Luna::PushObject( L, sDerivedClassName, p ); } \ LUA_REGISTER_CLASS_BASIC( T, B ) #define LUA_REGISTER_CLASS_BASIC( T, B ) \ - template<> RString Luna::m_sClassName = #T; \ - template<> RString Luna::m_sBaseClassName = #B; \ + template<> std::string Luna::m_sClassName = #T; \ + template<> std::string Luna::m_sBaseClassName = #B; \ void T::PushSelf( lua_State *L ) { Luna::PushObject( L, Luna::m_sClassName, this ); } \ static Luna##T registera##T; \ /* Call PushSelf, so we always call the derived Luna::Push. */ \ - namespace LuaHelpers { template<> void Push( lua_State *L, T *const &pObject ) { if( pObject == NULL ) lua_pushnil(L); else pObject->PushSelf( L ); } } + namespace LuaHelpers { template<> void Push( lua_State *L, T *const &pObject ) { if( pObject == nullptr ) lua_pushnil(L); else pObject->PushSelf( L ); } } #define DEFINE_METHOD( method_name, expr ) \ static int method_name( T* p, lua_State *L ) { LuaHelpers::Push( L, p->expr ); return 1; } @@ -171,7 +173,7 @@ static int get_##method_name(T* p, lua_State* L) \ } \ static int set_##method_name(T* p, lua_State* L) \ { \ - p->bool_name= lua_toboolean(L, 1); \ + p->bool_name= lua_toboolean(L, 1) != 0; \ COMMON_RETURN_SELF; \ } @@ -183,7 +185,7 @@ static int get_##bool_name(T* p, lua_State* L) \ } \ static int set_##bool_name(T* p, lua_State* L) \ { \ - p->set_##bool_name(lua_toboolean(L, 1)); \ + p->set_##bool_name(lua_toboolean(L, 1) != 0); \ COMMON_RETURN_SELF; \ } @@ -199,6 +201,18 @@ static int set_##method_name(T* p, lua_State* L) \ COMMON_RETURN_SELF; \ } +#define GET_SET_INT_METHOD(method_name, int_name) \ +static int get_##method_name(T* p, lua_State* L) \ +{ \ + lua_pushinteger(L, p->int_name); \ + return 1; \ +} \ +static int set_##method_name(T* p, lua_State* L) \ +{ \ + p->int_name= IArg(1); \ + COMMON_RETURN_SELF; \ +} + #define GETTER_SETTER_FLOAT_METHOD(float_name) \ static int get_##float_name(T* p, lua_State* L) \ { \ @@ -211,10 +225,43 @@ static int set_##float_name(T* p, lua_State* L) \ COMMON_RETURN_SELF; \ } +#define GET_SET_ENUM_METHOD(method_name, enum_name, val_name) \ +static int get_##method_name(T* p, lua_State* L) \ +{ \ + Enum::Push(L, p->val_name); \ + return 1; \ +} \ +static int set_##method_name(T* p, lua_State* L) \ +{ \ + p->val_name= Enum::Check(L, 1); \ + COMMON_RETURN_SELF; \ +} + +#define GETTER_SETTER_ENUM_METHOD(enum_name, val_name) \ +static int get_##val_name(T* p, lua_State* L) \ +{ \ + Enum::Push(L, p->get_##val_name()); \ + return 1; \ +} \ +static int set_##val_name(T* p, lua_State* L) \ +{ \ + p->set_##val_name(Enum::Check(L, 1)); \ + COMMON_RETURN_SELF; \ +} + #define ADD_METHOD( method_name ) \ AddMethod( #method_name, method_name ) #define ADD_GET_SET_METHODS(method_name) \ ADD_METHOD(get_##method_name); ADD_METHOD(set_##method_name); +#define LUA_SET_MEMBER(member, arg_conv) \ +static int set_##member(T* p, lua_State* L) \ +{ \ + p->m_##member= arg_conv(1); \ + COMMON_RETURN_SELF; \ +} +#define GET_SET_MEMBER(member, arg_conv) \ +DEFINE_METHOD(get_##member, m_##member); \ +LUA_SET_MEMBER(member, arg_conv); #define LUA_REGISTER_NAMESPACE( T ) \ static void Register##T( lua_State *L ) { luaL_register( L, #T, T##Table ); lua_pop( L, 1 ); } \ @@ -232,7 +279,7 @@ float FArgGTEZero(lua_State* L, int index); /* * (c) 2001-2005 Peter Shook, Chris Danford, Glenn Maynard * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -242,7 +289,7 @@ float FArgGTEZero(lua_State* L, int index); * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF diff --git a/src/LuaExpressionTransform.cpp b/src/LuaExpressionTransform.cpp index 7ddeb91d56..7b1c063f20 100644 --- a/src/LuaExpressionTransform.cpp +++ b/src/LuaExpressionTransform.cpp @@ -15,18 +15,26 @@ LuaExpressionTransform::~LuaExpressionTransform() void LuaExpressionTransform::SetFromReference( const LuaReference &ref ) { m_exprTransformFunction = ref; + if(ref.GetLuaType() != LUA_TFUNCTION) + { + LuaHelpers::ReportScriptError("Ignoring invalid transform function."); + m_exprTransformFunction.SetFromNil(); + } } void LuaExpressionTransform::TransformItemDirect( Actor &a, float fPositionOffsetFromCenter, int iItemIndex, int iNumItems ) const { + if(m_exprTransformFunction.IsNil()) + { + return; + } Lua *L = LUA->Get(); m_exprTransformFunction.PushSelf( L ); - ASSERT( !lua_isnil(L, -1) ); a.PushSelf( L ); LuaHelpers::Push( L, fPositionOffsetFromCenter ); LuaHelpers::Push( L, iItemIndex ); LuaHelpers::Push( L, iNumItems ); - RString error= "Lua error in Transform function: "; + std::string error= "Lua error in Transform function: "; LuaHelpers::RunScriptOnStack(L, error, 4, 0, true); LUA->Release(L); } @@ -35,7 +43,7 @@ const Actor::TweenState &LuaExpressionTransform::GetTransformCached( float fPosi { PositionOffsetAndItemIndex key = { fPositionOffsetFromCenter, iItemIndex }; - map::const_iterator iter = m_mapPositionToTweenStateCache.find( key ); + auto iter = m_mapPositionToTweenStateCache.find( key ); if( iter != m_mapPositionToTweenStateCache.end() ) return iter->second; @@ -68,7 +76,7 @@ void LuaExpressionTransform::TransformItemCached( Actor &a, float fPositionOffse /* * (c) 2003-2004 Chris Danford * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -78,7 +86,7 @@ void LuaExpressionTransform::TransformItemCached( Actor &a, float fPositionOffse * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF diff --git a/src/LuaExpressionTransform.h b/src/LuaExpressionTransform.h index ecdb1bfca2..8b76351ac0 100644 --- a/src/LuaExpressionTransform.h +++ b/src/LuaExpressionTransform.h @@ -41,7 +41,7 @@ class LuaExpressionTransform return iItemIndex < other.iItemIndex; } }; - mutable map m_mapPositionToTweenStateCache; + mutable std::map m_mapPositionToTweenStateCache; }; #endif @@ -49,7 +49,7 @@ class LuaExpressionTransform /* * (c) 2003-2004 Chris Danford * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -59,7 +59,7 @@ class LuaExpressionTransform * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF diff --git a/src/LuaManager.cpp b/src/LuaManager.cpp index 72bcfc70be..e16ab17381 100644 --- a/src/LuaManager.cpp +++ b/src/LuaManager.cpp @@ -5,7 +5,6 @@ #include "RageLog.h" #include "RageFile.h" #include "RageThreads.h" -#include "Foreach.h" #include "arch/Dialog/Dialog.h" #include "XmlFile.h" #include "Command.h" @@ -18,17 +17,23 @@ #include #include #include +#include +#include -LuaManager *LUA = NULL; +#include "stringtest.h" + +using std::vector; + +LuaManager *LUA = nullptr; struct Impl { Impl(): g_pLock("Lua") {} vector g_FreeStateList; - map g_ActiveStates; + std::map g_ActiveStates; RageMutex g_pLock; }; -static Impl *pImpl = NULL; +static Impl *pImpl = nullptr; #if defined(_MSC_VER) /* "interaction between '_setjmp' and C++ object destruction is non-portable" @@ -39,66 +44,376 @@ static Impl *pImpl = NULL; /** @brief Utilities for working with Lua. */ namespace LuaHelpers { - template<> void Push( lua_State *L, const bool &Object ); - template<> void Push( lua_State *L, const float &Object ); - template<> void Push( lua_State *L, const int &Object ); - template<> void Push( lua_State *L, const RString &Object ); + template<> + bool FromStack(Lua * L, RString & object, int offset); + template<> void Push(lua_State* L, bool const& object) + { + lua_pushboolean(L, object); + } + template<> void Push(lua_State* L, float const& object) + { + lua_pushnumber(L, object); + } + template<> void Push(lua_State* L, double const& object) + { + lua_pushnumber(L, object); + } + template<> void Push(lua_State* L, int const& object) + { + lua_pushinteger(L, object); + } + template<> void Push(lua_State* L, unsigned int const& object) + { + lua_pushnumber(L, static_cast(object)); + } + template<> void Push(lua_State* L, unsigned long const& object) + { + lua_pushnumber(L, static_cast(object)); + } + template<> void Push(lua_State* L, std::string const& object) + { + lua_pushlstring(L, object.data(), object.size()); + } + template<> void Push(lua_State* L, RString const& object) + { + lua_pushlstring(L, object.data(), object.size()); + } - template<> bool FromStack( Lua *L, bool &Object, int iOffset ); - template<> bool FromStack( Lua *L, float &Object, int iOffset ); - template<> bool FromStack( Lua *L, int &Object, int iOffset ); - template<> bool FromStack( Lua *L, RString &Object, int iOffset ); + template<> bool FromStack(Lua* L, bool& object, int offset) + { + object = lua_toboolean(L, offset) != 0; + return true; + } + template<> bool FromStack(Lua* L, float& object, int offset) + { + object = static_cast(lua_tonumber(L, offset)); + return true; + } + template<> bool FromStack(Lua* L, double& object, int offset) + { + object = static_cast(lua_tonumber(L, offset)); + return true; + } + template<> bool FromStack(Lua* L, int& object, int offset) + { + object = lua_tointeger(L, offset); + return true; + } + template<> bool FromStack(Lua* L, unsigned int& object, int offset) + { + object = lua_tointeger(L, offset); + return true; + } + template<> bool FromStack(Lua* L, unsigned long& object, int offset) + { + object = lua_tointeger(L, offset); + return true; + } + template<> bool FromStack(Lua* L, std::string &object, int offset) + { + size_t len; + char const *cstr = lua_tolstring(L, offset, &len); + if (cstr != nullptr) + { + object.assign(cstr); + return true; + } + object.clear(); + return false; + } + template<> bool FromStack(Lua* L, RString &object, int offset) + { + size_t len; + char const *cstr = lua_tolstring(L, offset, &len); + if (cstr != nullptr) + { + object.assign(cstr); + return true; + } + object.clear(); + return false; + } + bool FromStack(Lua* L, char const *object, int offset) + { + std::string clean{object}; + return LuaHelpers::FromStack(L, clean, offset); + } bool InReportScriptError= false; } -void LuaManager::SetGlobal( const RString &sName, int val ) +void LuaManager::SetGlobal( const std::string &sName, int val ) { Lua *L = Get(); LuaHelpers::Push( L, val ); - lua_setglobal( L, sName ); + lua_setglobal( L, sName.c_str() ); Release( L ); } -void LuaManager::SetGlobal( const RString &sName, const RString &val ) +void LuaManager::SetGlobal( const std::string &sName, const std::string &val ) { Lua *L = Get(); LuaHelpers::Push( L, val ); - lua_setglobal( L, sName ); + lua_setglobal( L, sName.c_str() ); Release( L ); } -void LuaManager::UnsetGlobal( const RString &sName ) +void LuaManager::UnsetGlobal( const std::string &sName ) { Lua *L = Get(); lua_pushnil( L ); - lua_setglobal( L, sName ); + lua_setglobal( L, sName.c_str() ); Release( L ); } -/** @brief Utilities for working with Lua. */ -namespace LuaHelpers +bool LuaHelpers::string_can_be_lua_identifier(lua_State* L, std::string const& str) +{ + int original_top= lua_gettop(L); + lua_getfield(L, LUA_GLOBALSINDEX, "string"); + lua_getfield(L, -1, "match"); + int ret_start_index= lua_gettop(L); + lua_pushstring(L, str.c_str()); + lua_pushstring(L, "^[a-zA-Z_][a-zA-Z_0-9]*$"); + lua_call(L, 2, LUA_MULTRET); + if(lua_isnil(L, ret_start_index)) + { + lua_settop(L, original_top); + return false; + } + lua_settop(L, original_top); + return true; +} + +void LuaHelpers::push_lua_escaped_string(lua_State* L, std::string const& str) +{ + lua_getfield(L, LUA_GLOBALSINDEX, "string"); + int str_tab_ind= lua_gettop(L); + lua_getfield(L, -1, "format"); + lua_pushstring(L, "%q"); + lua_pushstring(L, str.c_str()); + lua_call(L, 2, 1); + lua_remove(L, str_tab_ind); +} + +static void write_lua_value_to_file(lua_State* L, int value_index, + RageFile* file, std::string const& indent, std::unordered_set& visited_tables, bool write_equals); +static void write_lua_table_to_file(lua_State* L, int table_index, + RageFile* file, std::string const& indent, std::unordered_set& visited_tables); + +static void write_lua_value_to_file(lua_State* L, int value_index, + RageFile* file, std::string const& indent, std::unordered_set& visited_tables, bool write_equals) { - template<> void Push( lua_State *L, const bool &Object ) { lua_pushboolean( L, Object ); } - template<> void Push( lua_State *L, const float &Object ) { lua_pushnumber( L, Object ); } - template<> void Push( lua_State *L, const int &Object ) { lua_pushinteger( L, Object ); } - template<> void Push( lua_State *L, const unsigned int &Object ) { lua_pushnumber( L, double(Object) ); } - template<> void Push( lua_State *L, const RString &Object ) { lua_pushlstring( L, Object.data(), Object.size() ); } - - template<> bool FromStack( Lua *L, bool &Object, int iOffset ) { Object = !!lua_toboolean( L, iOffset ); return true; } - template<> bool FromStack( Lua *L, float &Object, int iOffset ) { Object = static_cast(lua_tonumber( L, iOffset )); return true; } - template<> bool FromStack( Lua *L, int &Object, int iOffset ) { Object = lua_tointeger( L, iOffset ); return true; } - template<> bool FromStack( Lua *L, RString &Object, int iOffset ) - { - size_t iLen; - const char *pStr = lua_tolstring( L, iOffset, &iLen ); - if( pStr != NULL ) - Object.assign( pStr, iLen ); + if(write_equals) + { + file->Write("= "); + } + switch(lua_type(L, value_index)) + { + case LUA_TTABLE: + write_lua_table_to_file(L, value_index, file, indent, visited_tables); + break; + case LUA_TSTRING: + { + lua_getfield(L, LUA_GLOBALSINDEX, "string"); + lua_getfield(L, -1, "format"); + lua_pushstring(L, "%q"); + lua_pushvalue(L, value_index); + lua_call(L, 2, 1); + file->Write(lua_tostring(L, -1)); + lua_pop(L, 2); + } + break; + case LUA_TNUMBER: + { + double as_double= lua_tonumber(L, value_index); + int as_int= lua_tointeger(L, value_index); + double int_turned_double= static_cast(as_int); + std::string val_str; + if(fabs(as_double - int_turned_double) < .001) + { + val_str= ssprintf("%i", as_int); + } + else + { + val_str= ssprintf("%.6f", as_double); + } + file->Write(val_str); + } + break; + case LUA_TBOOLEAN: + if(lua_toboolean(L, value_index)) + { + file->Write("true"); + } + else + { + file->Write("false"); + } + break; + default: + break; + } + file->Write(","); +} + +static void write_lua_table_to_file(lua_State* L, int table_index, + RageFile* file, std::string const& indent, std::unordered_set& visited_tables) +{ + visited_tables.insert(lua_topointer(L, table_index)); + // Fields shall be saved strictly ordered by key type and value. + // String fields, double fields, int fields, bool fields. + // Other key types shall be considered nonsense and ignored. -Kyz + std::vector string_fields; + std::vector double_fields; + std::vector int_fields; + std::vector bool_fields; + lua_pushnil(L); + while(lua_next(L, table_index) != 0) + { + // Filter out anything that is not a table, string, number, or boolean + // with a switch. Accepted types use fallthrough, default uses continue. + switch(lua_type(L, -1)) + { + case LUA_TTABLE: + { + void const* sub_table= lua_topointer(L, -1); + auto entry= visited_tables.find(sub_table); + if(entry != visited_tables.end()) + { + lua_pop(L, 1); + continue; + } + } + case LUA_TSTRING: + case LUA_TNUMBER: + case LUA_TBOOLEAN: + break; + default: + lua_pop(L, 1); + continue; + } + int key_type= lua_type(L, -2); + switch(key_type) + { + case LUA_TSTRING: + string_fields.push_back(std::string(lua_tostring(L, -2))); + break; + case LUA_TBOOLEAN: + bool_fields.push_back(lua_toboolean(L, -2) != 0); + break; + case LUA_TNUMBER: + { + double as_double= lua_tonumber(L, -2); + int as_int= lua_tointeger(L, -2); + double int_turned_double= static_cast(as_int); + if(fabs(as_double - int_turned_double) < .001) + { + int_fields.push_back(as_int); + } + else + { + double_fields.push_back(as_double); + } + } + break; + default: + break; + } + lua_pop(L, 1); + } + std::sort(string_fields.begin(), string_fields.end()); + std::sort(double_fields.begin(), double_fields.end()); + std::sort(int_fields.begin(), int_fields.end()); + std::sort(bool_fields.begin(), bool_fields.end()); + file->Write("{\n"); + std::string subindent= indent + " "; + for(auto&& field : string_fields) + { + file->Write(subindent); + if(LuaHelpers::string_can_be_lua_identifier(L, field)) + { + file->Write(field); + } else - Object.clear(); + { + file->Write("["); + LuaHelpers::push_lua_escaped_string(L, field); + file->Write(lua_tostring(L, -1)); + file->Write("]"); + lua_pop(L, 1); + } + lua_getfield(L, table_index, field.c_str()); + write_lua_value_to_file(L, lua_gettop(L), file, subindent, visited_tables, true); + lua_pop(L, 1); + file->Write("\n"); + } + for(auto&& field : double_fields) + { + file->Write(subindent); + file->Write(ssprintf("[%.6f]", field)); + lua_pushnumber(L, field); + lua_gettable(L, table_index); + write_lua_value_to_file(L, lua_gettop(L), file, subindent, visited_tables, true); + lua_pop(L, 1); + file->Write("\n"); + } + int next_array_style_index= 1; + for(auto&& field : int_fields) + { + file->Write(subindent); + bool needs_equals= true; + if(field == next_array_style_index) + { + needs_equals= false; + ++next_array_style_index; + } + else + { + file->Write(ssprintf("[%i]", field)); + } + lua_pushnumber(L, field); + lua_gettable(L, table_index); + write_lua_value_to_file(L, lua_gettop(L), file, subindent, visited_tables, needs_equals); + lua_pop(L, 1); + file->Write("\n"); + } + for(auto&& field : bool_fields) + { + file->Write(subindent); + if(field) + { + file->Write("[true]"); + } + else + { + file->Write("[false]"); + } + lua_pushboolean(L, field); + lua_gettable(L, table_index); + write_lua_value_to_file(L, lua_gettop(L), file, subindent, visited_tables, true); + lua_pop(L, 1); + file->Write("\n"); + } + file->Write(indent); + file->Write("}"); +} - return pStr != NULL; +void LuaHelpers::save_lua_table_to_file(lua_State* L, int table_index, + std::string const& filename) +{ + RageFile* file= new RageFile; + if(!file->Open(filename, RageFile::WRITE)) + { + LuaHelpers::ReportScriptErrorFmt("Could not open %s to save lua data: %s", filename.c_str(), file->GetError().c_str()); + return; } + std::unordered_set visited_tables; + std::string indent; + file->Write("return "); + write_lua_table_to_file(L, table_index, file, indent, visited_tables); + file->Write("\n"); + file->Close(); + delete file; } void LuaHelpers::CreateTableFromArrayB( Lua *L, const vector &aIn ) @@ -124,6 +439,55 @@ void LuaHelpers::ReadArrayFromTableB( Lua *L, vector &aOut ) } } +void LuaHelpers::rec_print_table(lua_State* L, std::string const& name, std::string const& indent) +{ + switch(lua_type(L, -1)) + { + case LUA_TNIL: + LOG->Trace("%s%s: nil", indent.c_str(), name.c_str()); + break; + case LUA_TNUMBER: + LOG->Trace("%s%s number: %f", indent.c_str(), name.c_str(), lua_tonumber(L, -1)); + break; + case LUA_TBOOLEAN: + LOG->Trace("%s%s bool: %d", indent.c_str(), name.c_str(), lua_toboolean(L, -1)); + break; + case LUA_TSTRING: + LOG->Trace("%s%s string: %s", indent.c_str(), name.c_str(), lua_tostring(L, -1)); + break; + case LUA_TTABLE: + { + size_t tablen= lua_objlen(L, -1); + LOG->Trace("%s%s table: %zu", indent.c_str(), name.c_str(), tablen); + std::string subindent= indent + " "; + lua_pushnil(L); + while(lua_next(L, -2) != 0) + { + lua_pushvalue(L, -2); + std::string sub_name= lua_tostring(L, -1); + lua_pop(L, 1); + rec_print_table(L, sub_name, subindent); + lua_pop(L, 1); + } + } + break; + case LUA_TFUNCTION: + LOG->Trace("%s%s function:", indent.c_str(), name.c_str()); + break; + case LUA_TUSERDATA: + LOG->Trace("%s%s userdata:", indent.c_str(), name.c_str()); + break; + case LUA_TTHREAD: + LOG->Trace("%s%s thread:", indent.c_str(), name.c_str()); + break; + case LUA_TLIGHTUSERDATA: + LOG->Trace("%s%s lightuserdata:", indent.c_str(), name.c_str()); + break; + default: + break; + } +} + namespace { // Creates a table from an XNode and leaves it on the stack. @@ -132,23 +496,21 @@ namespace // create our base table lua_newtable( L ); - - FOREACH_CONST_Attr( pNode, pAttr ) + for (auto const &pAttr: pNode->m_attrs) { - lua_pushstring( L, pAttr->first ); // push key - pNode->PushAttrValue( L, pAttr->first ); // push value + lua_pushstring( L, pAttr.first.c_str() ); // push key + pNode->PushAttrValue( L, pAttr.first ); // push value //add key-value pair to our table lua_settable( L, -3 ); } - FOREACH_CONST_Child( pNode, c ) + FOREACH_CONST_Child(pNode, c) { - const XNode *pChild = c; - lua_pushstring( L, pChild->m_sName ); // push key + lua_pushstring( L, c->m_sName.c_str() ); // push key // push value (more correctly, build this child's table and leave it there) - CreateTableFromXNodeRecursive( L, pChild ); + CreateTableFromXNodeRecursive( L, c ); // add key-value pair to the table lua_settable( L, -3 ); @@ -164,34 +526,40 @@ void LuaHelpers::CreateTableFromXNode( Lua *L, const XNode *pNode ) static int GetLuaStack( lua_State *L ) { - RString sErr; + std::string sErr; LuaHelpers::Pop( L, sErr ); - + lua_Debug ar; - + for( int iLevel = 0; lua_getstack(L, iLevel, &ar); ++iLevel ) { if( !lua_getinfo(L, "nSluf", &ar) ) + { break; + } // The function is now on the top of the stack. const char *file = ar.source[0] == '@' ? ar.source + 1 : ar.short_src; const char *name; - vector vArgs; - + vector vArgs; + + auto logAndPop = [&](char const *luaName) { + auto *luaStr = lua_tostring(L, -1); + vArgs.push_back( ssprintf("%s = %s", luaName, luaStr != nullptr ? luaStr : "nil") ); + lua_pop( L, 1 ); // pop value + }; + if( !strcmp(ar.what, "C") ) { - for( int i = 1; i <= ar.nups && (name = lua_getupvalue(L, -1, i)) != NULL; ++i ) + for( int i = 1; i <= ar.nups && (name = lua_getupvalue(L, -1, i)) != nullptr; ++i ) { - vArgs.push_back( ssprintf("%s = %s", name, lua_tostring(L, -1)) ); - lua_pop( L, 1 ); // pop value + logAndPop(name); } } else { - for( int i = 1; (name = lua_getlocal(L, &ar, i)) != NULL; ++i ) + for( int i = 1; (name = lua_getlocal(L, &ar, i)) != nullptr; ++i ) { - vArgs.push_back( ssprintf("%s = %s", name, lua_tostring(L, -1)) ); - lua_pop( L, 1 ); // pop value + logAndPop(name); } } @@ -205,14 +573,21 @@ static int GetLuaStack( lua_State *L ) sErr += ssprintf( "\n%s:", file ); if( ar.currentline != -1 ) + { sErr += ssprintf( "%i:", ar.currentline ); - + } if( ar.name && ar.name[0] ) + { sErr += ssprintf( " %s", ar.name ); + } else if( !strcmp(ar.what, "main") || !strcmp(ar.what, "tail") || !strcmp(ar.what, "C") ) + { sErr += ssprintf( " %s", ar.what ); + } else + { sErr += ssprintf( " unknown" ); + } sErr += ssprintf( "(%s)", join(",", vArgs).c_str() ); } @@ -225,18 +600,18 @@ static int LuaPanic( lua_State *L ) { GetLuaStack( L ); - RString sErr; + std::string sErr; LuaHelpers::Pop( L, sErr ); RageException::Throw( "[Lua panic] %s", sErr.c_str() ); } // Actor registration -static vector *g_vRegisterActorTypes = NULL; +static vector *g_vRegisterActorTypes = nullptr; void LuaManager::Register( RegisterWithLuaFn pfn ) { - if( g_vRegisterActorTypes == NULL ) + if( g_vRegisterActorTypes == nullptr ) g_vRegisterActorTypes = new vector; g_vRegisterActorTypes->push_back( pfn ); @@ -249,7 +624,7 @@ LuaManager::LuaManager() LUA = this; // so that LUA is available when we call the Register functions lua_State *L = lua_open(); - ASSERT( L != NULL ); + ASSERT( L != nullptr ); lua_atpanic( L, LuaPanic ); m_pLuaMain = L; @@ -321,7 +696,7 @@ void LuaManager::Release( Lua *&p ) if( bDoUnlock ) pImpl->g_pLock.Unlock(); - p = NULL; + p = nullptr; } /* @@ -334,7 +709,7 @@ void LuaManager::Release( Lua *&p ) * Lua *L = LUA->Get(); // acquires L and locks Lua * lua_newtable(L); // does something with Lua * LUA->YieldLua(); // unlocks Lua for lengthy operation; L is still owned, but can't be used - * RString s = ReadFile("/filename.txt"); // time-consuming operation; other threads may use Lua in the meantime + * std::string s = ReadFile("/filename.txt"); // time-consuming operation; other threads may use Lua in the meantime * LUA->UnyieldLua(); // relock Lua * lua_pushstring( L, s ); // finish working with it * LUA->Release( L ); // release L and unlock Lua @@ -368,17 +743,16 @@ void LuaManager::RegisterTypes() if( g_vRegisterActorTypes ) { - for( unsigned i=0; isize(); i++ ) + for (auto *actorType: *g_vRegisterActorTypes) { - RegisterWithLuaFn fn = (*g_vRegisterActorTypes)[i]; - fn( L ); + actorType(L); } } Release( L ); } -LuaThreadVariable::LuaThreadVariable( const RString &sName, const RString &sValue ) +LuaThreadVariable::LuaThreadVariable( const std::string &sName, const std::string &sValue ) { m_Name = new LuaReference; m_pOldValue = new LuaReference; @@ -391,7 +765,7 @@ LuaThreadVariable::LuaThreadVariable( const RString &sName, const RString &sValu LUA->Release( L ); } -LuaThreadVariable::LuaThreadVariable( const RString &sName, const LuaReference &Value ) +LuaThreadVariable::LuaThreadVariable( const std::string &sName, const LuaReference &Value ) { m_Name = new LuaReference; m_pOldValue = new LuaReference; @@ -419,7 +793,7 @@ LuaThreadVariable::LuaThreadVariable( lua_State *L ) lua_pop( L, 1 ); } -RString LuaThreadVariable::GetCurrentThreadIDString() +std::string LuaThreadVariable::GetCurrentThreadIDString() { uint64_t iID = RageThread::GetCurrentThreadID(); return ssprintf( "%08x%08x", uint32_t(iID >> 32), uint32_t(iID) ); @@ -439,7 +813,7 @@ bool LuaThreadVariable::PushThreadTable( lua_State *L, bool bCreate ) lua_setfield( L, LUA_REGISTRYINDEX, "LuaThreadVariableTable" ); } - RString sThreadIDString = GetCurrentThreadIDString(); + std::string sThreadIDString = GetCurrentThreadIDString(); LuaHelpers::Push( L, sThreadIDString ); lua_gettable( L, -2 ); if( lua_isnil(L, -1) ) @@ -547,8 +921,8 @@ namespace { struct LClass { - RString m_sBaseName; - vector m_vMethods; + std::string m_sBaseName; + vector m_vMethods; }; } @@ -563,18 +937,18 @@ XNode *LuaHelpers::GetLuaInformation() XNode *pEnumsNode = pLuaNode->AppendChild( "Enums" ); XNode *pConstantsNode = pLuaNode->AppendChild( "Constants" ); - vector vFunctions; - map mClasses; - map > mNamespaces; - map mSingletons; - map mConstants; - map mStringConstants; - map > mEnums; + vector vFunctions; + std::map mClasses; + std::map > mNamespaces; + std::map mSingletons; + std::map mConstants; + std::map mStringConstants; + std::map > mEnums; Lua *L = LUA->Get(); FOREACH_LUATABLE( L, LUA_GLOBALSINDEX ) { - RString sKey; + std::string sKey; LuaHelpers::Pop( L, sKey ); switch( lua_type(L, -1) ) @@ -603,7 +977,7 @@ XNode *LuaHelpers::GetLuaInformation() // Get methods. FOREACH_LUATABLE( L, -1 ) { - RString sMethod; + std::string sMethod; if( LuaHelpers::FromStack(L, sMethod, -1) ) c.m_vMethods.push_back( sMethod ); } @@ -616,7 +990,7 @@ XNode *LuaHelpers::GetLuaInformation() { if( !luaL_callmeta(L, -1, "__type") ) break; - RString sType; + std::string sType; if( !LuaHelpers::Pop(L, sType) ) break; if( sType == "Enum" ) @@ -652,19 +1026,30 @@ XNode *LuaHelpers::GetLuaInformation() lua_getfield( L, -1, "loaded" ); ASSERT( lua_istable(L, -1) ); - //const RString BuiltInPackages[] = { "_G", "coroutine", "debug", "math", "package", "string", "table" }; - const RString BuiltInPackages[] = { "_G", "coroutine", "debug", "math", "package", "string", "table" }; - const RString *const end = BuiltInPackages+ARRAYLEN(BuiltInPackages); + //const std::string BuiltInPackages[] = { "_G", "coroutine", "debug", "math", "package", "string", "table" }; + std::array const BuiltInPackages = + { + { + "_G", + "coroutine", + "debug", + "math", + "package", + "string", + "table" + } + }; + auto endIter = BuiltInPackages.end(); FOREACH_LUATABLE( L, -1 ) { - RString sNamespace; + std::string sNamespace; LuaHelpers::Pop( L, sNamespace ); - if( find(BuiltInPackages, end, sNamespace) != end ) + if( find(BuiltInPackages.begin(), endIter, sNamespace) != endIter ) continue; - vector &vNamespaceFunctions = mNamespaces[sNamespace]; + vector &vNamespaceFunctions = mNamespaces[sNamespace]; FOREACH_LUATABLE( L, -1 ) { - RString sFunction; + std::string sFunction; LuaHelpers::Pop( L, sFunction ); vNamespaceFunctions.push_back( sFunction ); } @@ -676,58 +1061,58 @@ XNode *LuaHelpers::GetLuaInformation() /* Globals */ sort( vFunctions.begin(), vFunctions.end() ); - FOREACH_CONST( RString, vFunctions, func ) + for (auto const &func: vFunctions) { XNode *pFunctionNode = pGlobalsNode->AppendChild( "Function" ); - pFunctionNode->AppendAttr( "name", *func ); + pFunctionNode->AppendAttr( "name", func ); } /* Classes */ - FOREACHM_CONST( RString, LClass, mClasses, c ) + for (auto const &c: mClasses) { XNode *pClassNode = pClassesNode->AppendChild( "Class" ); - pClassNode->AppendAttr( "name", c->first ); - if( !c->second.m_sBaseName.empty() ) - pClassNode->AppendAttr( "base", c->second.m_sBaseName ); - FOREACH_CONST( RString, c->second.m_vMethods, m ) + pClassNode->AppendAttr( "name", c.first ); + if( !c.second.m_sBaseName.empty() ) + pClassNode->AppendAttr( "base", c.second.m_sBaseName ); + for (auto const &m: c.second.m_vMethods) { XNode *pMethodNode = pClassNode->AppendChild( "Function" ); - pMethodNode->AppendAttr( "name", *m ); + pMethodNode->AppendAttr( "name", m ); } } /* Singletons */ - FOREACHM_CONST( RString, RString, mSingletons, s ) + for (auto const &s: mSingletons) { - if( mClasses.find(s->first) != mClasses.end() ) + if( mClasses.find(s.first) != mClasses.end() ) continue; XNode *pSingletonNode = pSingletonsNode->AppendChild( "Singleton" ); - pSingletonNode->AppendAttr( "name", s->first ); - pSingletonNode->AppendAttr( "class", s->second ); + pSingletonNode->AppendAttr( "name", s.first ); + pSingletonNode->AppendAttr( "class", s.second ); } /* Namespaces */ - for( map >::const_iterator iter = mNamespaces.begin(); iter != mNamespaces.end(); ++iter ) + for (auto &iter: mNamespaces) { XNode *pNamespaceNode = pNamespacesNode->AppendChild( "Namespace" ); - const vector &vNamespace = iter->second; - pNamespaceNode->AppendAttr( "name", iter->first ); + const vector &vNamespace = iter.second; + pNamespaceNode->AppendAttr( "name", iter.first ); - FOREACH_CONST( RString, vNamespace, func ) + for (auto const &func: vNamespace) { XNode *pFunctionNode = pNamespaceNode->AppendChild( "Function" ); - pFunctionNode->AppendAttr( "name", *func ); + pFunctionNode->AppendAttr( "name", func ); } } /* Enums */ - for( map >::const_iterator iter = mEnums.begin(); iter != mEnums.end(); ++iter ) + for (auto &iter: mEnums) { XNode *pEnumNode = pEnumsNode->AppendChild( "Enum" ); - const vector &vEnum = iter->second; - pEnumNode->AppendAttr( "name", iter->first ); + const vector &vEnum = iter.second; + pEnumNode->AppendAttr( "name", iter.first ); for( unsigned i = 0; i < vEnum.size(); ++i ) { @@ -738,36 +1123,63 @@ XNode *LuaHelpers::GetLuaInformation() } /* Constants, String Constants */ - FOREACHM_CONST( RString, float, mConstants, c ) + for (auto const &c: mConstants) { XNode *pConstantNode = pConstantsNode->AppendChild( "Constant" ); - pConstantNode->AppendAttr( "name", c->first ); - if( c->second == truncf(c->second) ) - pConstantNode->AppendAttr( "value", static_cast(c->second) ); + pConstantNode->AppendAttr( "name", c.first ); + if( c.second == std::trunc(c.second) ) + { + pConstantNode->AppendAttr( "value", static_cast(c.second) ); + } else - pConstantNode->AppendAttr( "value", c->second ); + { + pConstantNode->AppendAttr( "value", c.second ); + } } - FOREACHM_CONST( RString, RString, mStringConstants, s ) + + for (auto const &s: mStringConstants) { XNode *pConstantNode = pConstantsNode->AppendChild( "Constant" ); - pConstantNode->AppendAttr( "name", s->first ); - pConstantNode->AppendAttr( "value", ssprintf("'%s'", s->second.c_str()) ); + pConstantNode->AppendAttr( "name", s.first ); + pConstantNode->AppendAttr( "value", ssprintf("'%s'", s.second.c_str()) ); } return pLuaNode; } -bool LuaHelpers::RunScriptFile( const RString &sFile ) +bool LuaHelpers::run_script_file_in_state(lua_State* L, + std::string const& filename, int return_values, bool blank_env) +{ + std::string script; + if(!GetFileContents(filename, script)) + { + for(int i= 0; i < return_values; ++i) + { + lua_pushnil(L); + } + return false; + } + std::string err; + if(!LuaHelpers::RunScript(L, script, "@" + filename, err, 0, return_values, false, blank_env)) + { + err= ssprintf("Lua runtime error: %s", err.c_str()); + LuaHelpers::ReportScriptError(err); + return false; + } + return true; +} + +bool LuaHelpers::RunScriptFile(const std::string &sFile, bool blank_env) { - RString sScript; + std::string sScript; if( !GetFileContents(sFile, sScript) ) return false; Lua *L = LUA->Get(); - RString sError; - if( !LuaHelpers::RunScript( L, sScript, "@" + sFile, sError, 0 ) ) + std::string sError; + if(!LuaHelpers::RunScript(L, sScript, "@" + sFile, sError, 0, 0, false, blank_env)) { LUA->Release( L ); sError = ssprintf( "Lua runtime error: %s", sError.c_str() ); @@ -780,10 +1192,10 @@ bool LuaHelpers::RunScriptFile( const RString &sFile ) } -bool LuaHelpers::LoadScript( Lua *L, const RString &sScript, const RString &sName, RString &sError ) +bool LuaHelpers::LoadScript( Lua *L, const std::string &sScript, const std::string &sName, std::string &sError ) { // load string - int ret = luaL_loadbuffer( L, sScript.data(), sScript.size(), sName ); + int ret = luaL_loadbuffer( L, sScript.data(), sScript.size(), sName.c_str() ); if( ret ) { LuaHelpers::Pop( L, sError ); @@ -793,14 +1205,17 @@ bool LuaHelpers::LoadScript( Lua *L, const RString &sScript, const RString &sNam return true; } -void LuaHelpers::ScriptErrorMessage(RString const& Error) +void LuaHelpers::ScriptErrorMessage(std::string const& Error) { - Message msg("ScriptError"); - msg.SetParam("message", Error); - MESSAGEMAN->Broadcast(msg); + if (MESSAGEMAN != nullptr) + { + Message msg("ScriptError"); + msg.SetParam("message", Error); + MESSAGEMAN->Broadcast(msg); + } } -Dialog::Result LuaHelpers::ReportScriptError(RString const& Error, RString ErrorType, bool UseAbort) +Dialog::Result LuaHelpers::ReportScriptError(std::string const& Error, std::string ErrorType, bool UseAbort) { // Protect from a recursion loop resulting from a mistake in the error reporting lua. if(!InReportScriptError) @@ -812,30 +1227,32 @@ Dialog::Result LuaHelpers::ReportScriptError(RString const& Error, RString Error LOG->Warn( "%s", Error.c_str()); if(UseAbort) { - RString with_correct= Error + " Correct this and click Retry, or Cancel to break."; + std::string with_correct= Error + " Correct this and click Retry, or Cancel to break."; return Dialog::AbortRetryIgnore(with_correct, ErrorType); } //Dialog::OK(Error, ErrorType); return Dialog::ok; } -// For convenience when replacing uses of LOG->Warn. -void LuaHelpers::ReportScriptErrorFmt(const char *fmt, ...) +void infinite_loop_preventer(lua_State* L, lua_Debug*) { - va_list va; - va_start( va, fmt ); - RString Buff = vssprintf( fmt, va ); - va_end( va ); - ReportScriptError(Buff); + luaL_error(L, "Infinite loop detected, too many instructions."); } -bool LuaHelpers::RunScriptOnStack( Lua *L, RString &Error, int Args, int ReturnValues, bool ReportError ) +bool LuaHelpers::RunScriptOnStack(Lua *L, std::string &Error, int Args, + int ReturnValues, bool ReportError, bool blank_env) { + if(blank_env) + { + lua_newtable(L); + lua_setfenv(L, lua_gettop(L) - Args - 1); + } lua_pushcfunction( L, GetLuaStack ); // move the error function above the function and params int ErrFunc = lua_gettop(L) - Args - 1; lua_insert( L, ErrFunc ); + lua_sethook(L, infinite_loop_preventer, LUA_MASKCOUNT, 1000000000); // evaluate int ret = lua_pcall( L, Args, ReturnValues, ErrFunc ); @@ -843,7 +1260,7 @@ bool LuaHelpers::RunScriptOnStack( Lua *L, RString &Error, int Args, int ReturnV { if(ReportError) { - RString lerror; + std::string lerror; LuaHelpers::Pop( L, lerror ); Error+= lerror; ReportScriptError(Error); @@ -854,7 +1271,9 @@ bool LuaHelpers::RunScriptOnStack( Lua *L, RString &Error, int Args, int ReturnV } lua_remove( L, ErrFunc ); for( int i = 0; i < ReturnValues; ++i ) + { lua_pushnil( L ); + } return false; } @@ -862,9 +1281,11 @@ bool LuaHelpers::RunScriptOnStack( Lua *L, RString &Error, int Args, int ReturnV return true; } -bool LuaHelpers::RunScript( Lua *L, const RString &Script, const RString &Name, RString &Error, int Args, int ReturnValues, bool ReportError ) +bool LuaHelpers::RunScript(Lua *L, const std::string &Script, + const std::string &Name, std::string &Error, int Args, int ReturnValues, + bool ReportError, bool blank_env) { - RString lerror; + std::string lerror; if( !LoadScript(L, Script, Name, lerror) ) { Error+= lerror; @@ -874,29 +1295,32 @@ bool LuaHelpers::RunScript( Lua *L, const RString &Script, const RString &Name, } lua_pop( L, Args ); for( int i = 0; i < ReturnValues; ++i ) + { lua_pushnil( L ); + } return false; } // move the function above the params lua_insert( L, lua_gettop(L) - Args ); - return LuaHelpers::RunScriptOnStack( L, Error, Args, ReturnValues, ReportError ); + return LuaHelpers::RunScriptOnStack(L, Error, Args, ReturnValues, + ReportError, blank_env); } -bool LuaHelpers::RunExpression( Lua *L, const RString &sExpression, const RString &sName ) +bool LuaHelpers::RunExpression( Lua *L, const std::string &sExpression, const std::string &sName, bool blank_env) { - RString sError= ssprintf("Lua runtime error parsing \"%s\": ", sName.size()? sName.c_str():sExpression.c_str()); - if(!LuaHelpers::RunScript(L, "return " + sExpression, sName.empty()? RString("in"):sName, sError, 0, 1, true)) + std::string sError= ssprintf("Lua runtime error parsing \"%s\": ", sName.size()? sName.c_str():sExpression.c_str()); + if(!LuaHelpers::RunScript(L, "return " + sExpression, sName.empty()? std::string("in"):sName, sError, 0, 1, true, blank_env)) { return false; } return true; } -void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RString &sName, bool bLegacy ) +void LuaHelpers::ParseCommandList( Lua *L, const std::string &sCommands, const std::string &sName, bool bLegacy ) { - RString sLuaFunction; + std::string sLuaFunction; if( sCommands.size() > 0 && sCommands[0] == '\033' ) { // This is a compiled Lua chunk. Just pass it on directly. @@ -913,19 +1337,20 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri ParseCommands( sCommands, cmds, bLegacy ); // Convert cmds to a Lua function - ostringstream s; + std::ostringstream s; s << "return function(self)\n"; if( bLegacy ) s << "\tparent = self:GetParent();\n"; - FOREACH_CONST( Command, cmds.v, c ) + for (auto const &cmd: cmds.v) { - const Command& cmd = (*c); - RString sCmdName = cmd.GetName(); + std::string sCmdName = cmd.GetName(); if( bLegacy ) - sCmdName.MakeLower(); + { + sCmdName = Rage::make_lower(sCmdName); + } s << "\tself:" << sCmdName << "("; bool bFirstParamIsString = bLegacy && ( @@ -942,7 +1367,7 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri for( unsigned i=1; i "200" if( sArg[0] == '+' ) @@ -950,7 +1375,7 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri if( i==1 && bFirstParamIsString ) // string literal, legacy only { - sArg.Replace( "'", "\\'" ); // escape quote + Rage::replace(sArg, "'", "\\'" ); // escape quote s << "'" << sArg << "'"; } else if( sArg[0] == '#' ) // HTML color @@ -976,7 +1401,7 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri sLuaFunction = s.str(); } - RString sError; + std::string sError; if( !LuaHelpers::RunScript(L, sLuaFunction, sName, sError, 0, 1) ) LOG->Warn( "Compiling \"%s\": %s", sLuaFunction.c_str(), sError.c_str() ); @@ -985,9 +1410,9 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri /* Like luaL_typerror, but without the special case for argument 1 being "self" * in method calls, so we give a correct error message after we remove self. */ -int LuaHelpers::TypeError( Lua *L, int iArgNo, const char *szName ) +int LuaHelpers::TypeError( Lua *L, int iArgNo, std::string const &szName ) { - RString sType; + std::string sType; luaL_pushtype( L, iArgNo ); LuaHelpers::Pop( L, sType ); @@ -995,13 +1420,13 @@ int LuaHelpers::TypeError( Lua *L, int iArgNo, const char *szName ) if( !lua_getstack( L, 0, &debug ) ) { return luaL_error( L, "invalid type (%s expected, got %s)", - szName, sType.c_str() ); + szName.c_str(), sType.c_str() ); } else { lua_getinfo( L, "n", &debug ); return luaL_error( L, "bad argument #%d to \"%s\" (%s expected, got %s)", - iArgNo, debug.name? debug.name:"(unknown)", szName, sType.c_str() ); + iArgNo, debug.name? debug.name:"(unknown)", szName.c_str(), sType.c_str() ); } } @@ -1026,7 +1451,9 @@ namespace { int iArgs = lua_tointeger( L, lua_upvalueindex(1) ); for( int i = 0; i < iArgs; ++i ) + { lua_pushvalue( L, lua_upvalueindex(i+2) ); + } return iArgs; } } @@ -1040,20 +1467,16 @@ void LuaHelpers::PushValueFunc( lua_State *L, int iArgs ) } #include "ProductInfo.h" -LuaFunction( ProductFamily, (RString) PRODUCT_FAMILY ); -LuaFunction( ProductVersion, (RString) product_version ); -LuaFunction( ProductID, (RString) PRODUCT_ID ); +LuaFunction( ProductFamily, (std::string) PRODUCT_FAMILY ); +LuaFunction( ProductVersion, (std::string) product_version ); +LuaFunction( ProductID, (std::string) PRODUCT_ID ); extern char const * const version_date; extern char const * const version_time; -LuaFunction( VersionDate, (RString) version_date ); -LuaFunction( VersionTime, (RString) version_time ); +LuaFunction( VersionDate, (std::string) version_date ); +LuaFunction( VersionTime, (std::string) version_time ); -static float scale( float x, float l1, float h1, float l2, float h2 ) -{ - return SCALE( x, l1, h1, l2, h2 ); -} -LuaFunction( scale, scale(FArg(1), FArg(2), FArg(3), FArg(4), FArg(5)) ); +LuaFunction( scale, SCALE(FArg(1), FArg(2), FArg(3), FArg(4), FArg(5)) ); LuaFunction( clamp, clamp(FArg(1), FArg(2), FArg(3)) ); @@ -1062,37 +1485,37 @@ namespace { static int Trace( lua_State *L ) { - RString sString = SArg(1); + std::string sString = SArg(1); LOG->Trace( "%s", sString.c_str() ); return 0; } static int Warn( lua_State *L ) { - RString sString = SArg(1); + std::string sString = SArg(1); LOG->Warn( "%s", sString.c_str() ); return 0; } - static int Flush( lua_State *L ) + static int Flush(lua_State*) { LOG->Flush(); return 0; } static int CheckType( lua_State *L ) { - RString sType = SArg(1); + std::string sType = SArg(1); bool bRet = LuaBinding::CheckLuaObjectType( L, 2, sType ); LuaHelpers::Push( L, bRet ); return 1; } static int ReadFile( lua_State *L ) { - RString sPath = SArg(1); + std::string sPath = SArg(1); /* Release Lua while we call GetFileContents, so we don't access * it while we read from the disk. */ LUA->YieldLua(); - RString sFileContents; + std::string sFileContents; bool bRet = GetFileContents( sPath, sFileContents ); LUA->UnyieldLua(); @@ -1133,8 +1556,10 @@ namespace lua_call( L, iArgs, LUA_MULTRET ); int iVals = lua_gettop(L); - FOREACH( LuaThreadVariable *, apVars, v ) - delete *v; + for (auto *v: apVars) + { + delete v; + } return iVals; } @@ -1148,8 +1573,8 @@ namespace static int ReportScriptError(lua_State* L) { - RString error= "Script error occurred."; - RString error_type= "LUA_ERROR"; + std::string error= "Script error occurred."; + std::string error_type= "LUA_ERROR"; if(lua_isstring(L, 1)) { error= SArg(1); @@ -1161,6 +1586,22 @@ namespace LuaHelpers::ReportScriptError(error, error_type); return 0; } + static int save_lua_table(lua_State* L) + { + std::string filename= SArg(1); + if(lua_type(L, 2) != LUA_TTABLE) + { + luaL_error(L, "Second arg to save_lua_table must be a table."); + } + LuaHelpers::save_lua_table_to_file(L, 2, filename); + return 0; + } + static int load_config_lua(lua_State* L) + { + std::string filename= SArg(1); + LuaHelpers::run_script_file_in_state(L, filename, 1, true); + return 1; + } const luaL_Reg luaTable[] = { @@ -1172,7 +1613,9 @@ namespace LIST_METHOD( RunWithThreadVariables ), LIST_METHOD( GetThreadVariable ), LIST_METHOD( ReportScriptError ), - { NULL, NULL } + LIST_METHOD(save_lua_table), + LIST_METHOD(load_config_lua), + { nullptr, nullptr } }; } @@ -1181,7 +1624,7 @@ LUA_REGISTER_NAMESPACE( lua ) /* * (c) 2004-2006 Glenn Maynard, Steve Checkoway * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -1191,7 +1634,7 @@ LUA_REGISTER_NAMESPACE( lua ) * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF diff --git a/src/LuaManager.h b/src/LuaManager.h index 9cd86b90c1..b82db6d1bc 100644 --- a/src/LuaManager.h +++ b/src/LuaManager.h @@ -17,6 +17,7 @@ extern "C" // For Dialog::Result #include "arch/Dialog/Dialog.h" +#include "RageUtil.h" class LuaManager { @@ -39,9 +40,9 @@ class LuaManager // There's no harm in registering when already registered. void RegisterTypes(); - void SetGlobal( const RString &sName, int val ); - void SetGlobal( const RString &sName, const RString &val ); - void UnsetGlobal( const RString &sName ); + void SetGlobal( const std::string &sName, int val ); + void SetGlobal( const std::string &sName, const std::string &val ); + void UnsetGlobal( const std::string &sName ); private: lua_State *m_pLuaMain; @@ -59,42 +60,61 @@ namespace LuaHelpers /* Load the given script with the given name. On success, the resulting * chunk will be on the stack. On error, the error is stored in sError * and the stack is unchanged. */ - bool LoadScript( Lua *L, const RString &sScript, const RString &sName, RString &sError ); + bool LoadScript( Lua *L, const std::string &sScript, const std::string &sName, std::string &sError ); /* Report the error three ways: Broadcast message, Warn, and Dialog. */ /* If UseAbort is true, reports the error through Dialog::AbortRetryIgnore and returns the result. */ /* If UseAbort is false, reports the error through Dialog::OK and returns Dialog::ok. */ - Dialog::Result ReportScriptError(RString const& Error, RString ErrorType= "LUA_ERROR", bool UseAbort= false); + Dialog::Result ReportScriptError(std::string const& Error, std::string ErrorType= "LUA_ERROR", bool UseAbort= false); // Just the broadcast message part, for things that need to do the rest differently. - void ScriptErrorMessage(RString const& Error); - // For convenience when replacing uses of LOG->Warn. - void ReportScriptErrorFmt(const char *fmt, ...); + void ScriptErrorMessage(std::string const& Error); + + /** @brief A convenience method to use when replacing uses of LOG->Warn. */ + template + void ReportScriptErrorFmt(std::string const &msg, Args const & ...args) + { + std::string result = ssprintf(msg.c_str(), args...); + ReportScriptError(result); + } /* Run the function with arguments at the top of the stack, with the given * number of arguments. The specified number of return values are left on - * the Lua stack. On error, nils are left on the stack, sError is set and + * the Lua stack. On error, nils are left on the stack, sError is set and * false is returned. * If ReportError is true, Error should contain the string to prepend * when reporting. The error is reported through LOG->Warn and * SCREENMAN->SystemMessage. */ - bool RunScriptOnStack( Lua *L, RString &Error, int Args = 0, int ReturnValues = 0, bool ReportError = false ); + bool RunScriptOnStack(Lua *L, std::string &Error, int Args = 0, + int ReturnValues = 0, bool ReportError = false, bool blank_env= false); /* LoadScript the given script, and RunScriptOnStack it. * iArgs arguments are at the top of the stack. */ - bool RunScript( Lua *L, const RString &Script, const RString &Name, RString &Error, int Args = 0, int ReturnValues = 0, bool ReportError = false ); + bool RunScript(Lua *L, const std::string &Script, const std::string &Name, + std::string &Error, int Args = 0, int ReturnValues = 0, + bool ReportError = false, bool blank_env= false); /* Run the given expression, returning a single value, and leave the return * value on the stack. On error, push nil. */ - bool RunExpression( Lua *L, const RString &sExpression, const RString &sName = "" ); + bool RunExpression(Lua *L, const std::string &sExpression, + const std::string &sName = "", bool blank_env= false); + + bool RunScriptFile(const std::string &sFile, bool blank_env= false); - bool RunScriptFile( const RString &sFile ); + bool run_script_file_in_state(lua_State* L, std::string const& filename, + int return_values, bool blank_env); + bool string_can_be_lua_identifier(lua_State* L, std::string const& str); + void push_lua_escaped_string(lua_State* L, std::string const& str); + // save_lua_table_to_file will only save bools, strings, and numbers. + // Nothing else in the lua table will be saved. + void save_lua_table_to_file(lua_State* L, int table_index, + std::string const& filename); /* Create a Lua array (a table with indices starting at 1) of the given vector, * and push it on the stack. */ - void CreateTableFromArrayB( Lua *L, const vector &aIn ); + void CreateTableFromArrayB( Lua *L, const std::vector &aIn ); // Create a Lua table with contents set from this XNode, then push it on the stack. void CreateTableFromXNode( Lua *L, const XNode *pNode ); @@ -104,9 +124,11 @@ namespace LuaHelpers void DeepCopy( lua_State *L ); // Read the table at the top of the stack back into a vector. - void ReadArrayFromTableB( Lua *L, vector &aOut ); + void ReadArrayFromTableB( Lua *L, std::vector &aOut ); - void ParseCommandList( lua_State *L, const RString &sCommands, const RString &sName, bool bLegacy ); + void rec_print_table(lua_State* L, std::string const& name, std::string const& indent); + + void ParseCommandList( lua_State *L, const std::string &sCommands, const std::string &sName, bool bLegacy ); XNode *GetLuaInformation(); @@ -120,6 +142,9 @@ namespace LuaHelpers template bool FromStack( lua_State *L, T &Object, int iOffset ); + // Not using a template for the c style string: found it tricky to use. + bool FromStack( lua_State *L, char const *Object, int iOffset ); + template bool Pop( lua_State *L, T &val ) { @@ -127,9 +152,9 @@ namespace LuaHelpers lua_pop( L, 1 ); return bRet; } - + template - void ReadArrayFromTable( vector &aOut, lua_State *L ) + void ReadArrayFromTable( std::vector &aOut, lua_State *L ) { luaL_checktype( L, -1, LUA_TTABLE ); @@ -143,7 +168,7 @@ namespace LuaHelpers lua_pop( L, 1 ); // pop nil } template - void CreateTableFromArray( const vector &aIn, lua_State *L ) + void CreateTableFromArray( const std::vector &aIn, lua_State *L ) { lua_newtable( L ); for( unsigned i = 0; i < aIn.size(); ++i ) @@ -153,15 +178,15 @@ namespace LuaHelpers } } - int TypeError( Lua *L, int narg, const char *tname ); + int TypeError( Lua *L, int narg, std::string const &tname ); inline int AbsIndex( Lua *L, int i ) { if( i > 0 || i <= LUA_REGISTRYINDEX ) return i; return lua_gettop( L ) + i + 1; } } class LuaThreadVariable { public: - LuaThreadVariable( const RString &sName, const RString &sValue ); - LuaThreadVariable( const RString &sName, const LuaReference &Value ); + LuaThreadVariable( const std::string &sName, const std::string &sValue ); + LuaThreadVariable( const std::string &sName, const LuaReference &Value ); LuaThreadVariable( lua_State *L ); // name and value are on stack ~LuaThreadVariable(); static void GetThreadVariable( lua_State *L ); @@ -172,22 +197,22 @@ class LuaThreadVariable void SetFromStack( lua_State *L ); int AdjustCount( lua_State *L, int iAdd ); static bool PushThreadTable( lua_State *L, bool bCreate ); - static RString GetCurrentThreadIDString(); + static std::string GetCurrentThreadIDString(); LuaReference *m_Name; LuaReference *m_pOldValue; - + // Swallow up warnings. If they must be used, define them. LuaThreadVariable& operator=(const LuaThreadVariable& rhs); }; /** * @brief Iterate over all elements in the table. - * - * For safety reasons, the key is pushed onto the stack and can be read (safely) - * as a string and popped or altered in any way. Stack management is handled - * automatically. That is, you need not remove all stack elements above the key. - * Once the loop exits normally, the top of the stack will be where it was before. + * + * For safety reasons, the key is pushed onto the stack and can be read (safely) + * as a string and popped or altered in any way. Stack management is handled + * automatically. That is, you need not remove all stack elements above the key. + * Once the loop exits normally, the top of the stack will be where it was before. * If you break out of the loop early, you need to handle that explicitly. */ #define FOREACH_LUATABLE(L,index) \ for( const int SM_UNIQUE_NAME(tab) = LuaHelpers::AbsIndex(L,index), \ @@ -240,7 +265,7 @@ inline bool TableContainsOnlyStrings(lua_State* L, int index) { // `key' is at index -2 and `value' at index -1 const char *pValue = lua_tostring(L, -1); - if(pValue == NULL) + if(pValue == nullptr) { // Was going to print an error to the log with the key that failed, // but didn't want to pull in RageLog. -Kyz @@ -260,7 +285,7 @@ inline bool TableContainsOnlyStrings(lua_State* L, int index) // SafeFArg is for places that need to get a number off the lua stack, but // can't risk an error being raised. IArg and luaL_optnumber would both raise // an error on a type mismatch. -Kyz -inline int SafeFArg(lua_State* L, int index, RString const& err, int def) +inline int SafeFArg(lua_State* L, int index, std::string const& err, int def) { if(lua_isnumber(L, index)) { @@ -270,6 +295,53 @@ inline int SafeFArg(lua_State* L, int index, RString const& err, int def) return def; } +inline double get_optional_double(lua_State* L, int index, char const* field, double def) +{ + double ret= def; + lua_getfield(L, index, field); + if(lua_isnumber(L, -1)) + { + ret= static_cast(lua_tonumber(L, -1)); + } + lua_pop(L, 1); + return ret; +} + +inline int get_optional_int(lua_State* L, int index, char const* field, int def) +{ + int ret= def; + lua_getfield(L, index, field); + if(lua_isnumber(L, -1)) + { + ret= static_cast(lua_tonumber(L, -1)); + } + lua_pop(L, 1); + return ret; +} + +inline bool get_optional_bool(lua_State* L, int index, char const* field) +{ + lua_getfield(L, index, field); + bool ret = lua_toboolean(L, -1) == 1; + lua_pop(L, 1); + return ret; +} + +inline bool value_is_in_table(lua_State* L, int value_index, int table_index) +{ + lua_pushnil(L); + while(lua_next(L, table_index) != 0) + { + if(lua_equal(L, value_index, -1)) + { + lua_pop(L, 2); + return true; + } + lua_pop(L, 1); + } + return false; +} + #define LuaFunction( func, expr ) \ int LuaFunc_##func( lua_State *L ); \ int LuaFunc_##func( lua_State *L ) { \ @@ -289,7 +361,7 @@ REGISTER_WITH_LUA_FUNCTION(LuaFunc_Register_##func_name); /* * (c) 2004 Glenn Maynard * All rights reserved. - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -299,7 +371,7 @@ REGISTER_WITH_LUA_FUNCTION(LuaFunc_Register_##func_name); * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF diff --git a/src/LuaReference.cpp b/src/LuaReference.cpp index 537337ca09..9db6ccf232 100644 --- a/src/LuaReference.cpp +++ b/src/LuaReference.cpp @@ -112,7 +112,7 @@ int LuaReference::GetLuaType() const void LuaReference::Unregister() { - if( LUA == NULL || m_iReference == LUA_NOREF ) + if( LUA == nullptr || m_iReference == LUA_NOREF ) return; // nothing to do Lua *L = LUA->Get(); @@ -121,7 +121,7 @@ void LuaReference::Unregister() m_iReference = LUA_NOREF; } -bool LuaReference::SetFromExpression( const RString &sExpression ) +bool LuaReference::SetFromExpression( const std::string &sExpression ) { Lua *L = LUA->Get(); @@ -132,7 +132,7 @@ bool LuaReference::SetFromExpression( const RString &sExpression ) return bSuccess; } -RString LuaReference::Serialize() const +std::string LuaReference::Serialize() const { /* Call Serialize(t), where t is our referenced object. */ Lua *L = LUA->Get(); @@ -148,9 +148,9 @@ RString LuaReference::Serialize() const /* The return value is a string, which we store in m_sSerializedData. */ const char *pString = lua_tostring( L, -1 ); - ASSERT_M( pString != NULL, "Serialize() didn't return a string" ); + ASSERT_M( pString != nullptr, "Serialize() didn't return a string" ); - RString sRet = pString; + std::string sRet = pString; lua_pop( L, 1 ); LUA->Release( L ); @@ -185,19 +185,19 @@ LuaTable::LuaTable() LUA->Release( L ); } -void LuaTable::Set( Lua *L, const RString &sKey ) +void LuaTable::Set( Lua *L, const std::string &sKey ) { int iTop = lua_gettop( L ); this->PushSelf( L ); lua_pushvalue( L, iTop ); // push the value - lua_setfield( L, -2, sKey ); + lua_setfield( L, -2, sKey.c_str() ); lua_settop( L, iTop-1 ); // remove all of the above } -void LuaTable::Get( Lua *L, const RString &sKey ) +void LuaTable::Get( Lua *L, const std::string &sKey ) { this->PushSelf( L ); - lua_getfield( L, -1, sKey ); + lua_getfield( L, -1, sKey.c_str() ); lua_remove( L, -2 ); // remove self } diff --git a/src/LuaReference.h b/src/LuaReference.h index a847d5f743..bb920a08a7 100644 --- a/src/LuaReference.h +++ b/src/LuaReference.h @@ -24,6 +24,13 @@ class LuaReference SetFromStack(L); } + void swap(LuaReference& other) + { + LuaReference temp= *this; + *this= other; + other= temp; + } + /* Create a reference pointing to the item at the top of the stack, and pop * the stack. */ void SetFromStack( Lua *L ); @@ -32,7 +39,7 @@ class LuaReference /* Evaluate an expression that returns an object; store the object in a reference. * For example, evaluating "{ 1, 2, 3 }" will result in a reference to a table. * On success, return true. On error, set to nil and return false. */ - bool SetFromExpression( const RString &sExpression ); + bool SetFromExpression( const std::string &sExpression ); /** @brief Deep-copy tables, detaching this reference from any others. */ void DeepCopy(); @@ -55,7 +62,7 @@ class LuaReference /* Return the referenced type, or LUA_TNONE if not set. */ int GetLuaType() const; - RString Serialize() const; + std::string Serialize() const; template static LuaReference Create( const T &val ) @@ -94,11 +101,11 @@ class LuaTable: public LuaReference LuaTable(); /* Get the key with the given name, and push it on the stack. */ - void Get( Lua *L, const RString &sKey ); + void Get( Lua *L, const std::string &sKey ); /* Set a key by the given name to a value on the stack, and pop the value * off the stack. */ - void Set( Lua *L, const RString &sKey ); + void Set( Lua *L, const std::string &sKey ); }; #endif diff --git a/src/RageUtil.cpp b/src/RageUtil.cpp index c06fb54e10..174af63fd3 100644 --- a/src/RageUtil.cpp +++ b/src/RageUtil.cpp @@ -698,6 +698,60 @@ RString join( const RString &sDelimitor, vector::const_iterator begin, return sRet; } + +std::string join(const std::string &sDeliminator, const vector &sSource) +{ + if (sSource.empty()) + return std::string(); + + std::string sTmp; + size_t final_size = 0; + size_t delim_size = sDeliminator.size(); + for (size_t n = 0; n < sSource.size() - 1; ++n) + { + final_size += sSource[n].size() + delim_size; + } + final_size += sSource.back().size(); + sTmp.reserve(final_size); + + for (unsigned iNum = 0; iNum < sSource.size() - 1; iNum++) + { + sTmp += sSource[iNum]; + sTmp += sDeliminator; + } + sTmp += sSource.back(); + return sTmp; +} + +std::string join(const std::string &sDelimitor, vector::const_iterator begin, vector::const_iterator end) +{ + if (begin == end) + return std::string(); + + std::string sRet; + size_t final_size = 0; + size_t delim_size = sDelimitor.size(); + for (vector::const_iterator curr = begin; curr != end; ++curr) + { + final_size += curr->size(); + if (curr != end) + { + final_size += delim_size; + } + } + sRet.reserve(final_size); + + while (begin != end) + { + sRet += *begin; + ++begin; + if (begin != end) + sRet += sDelimitor; + } + + return sRet; +} + RString SmEscape( const RString &sUnescaped ) { return SmEscape( sUnescaped.c_str(), sUnescaped.size() ); @@ -1372,6 +1426,40 @@ bool GetFileContents( const RString &sPath, RString &sOut, bool bOneLine ) return true; } +bool GetFileContents(const std::string &sPath, std::string &sOut, bool bOneLine) +{ + // Don't warn if the file doesn't exist, but do warn if it exists and fails to open. + if (!IsAFile(sPath)) + return false; + + RageFile file; + if (!file.Open(sPath)) + { + LOG->Warn("GetFileContents(%s): %s", sPath.c_str(), file.GetError().c_str()); + return false; + } + + // todo: figure out how to make this UTF-8 safe. -aj + RString sData; + int iGot; + if (bOneLine) + iGot = file.GetLine(sData); + else + iGot = file.Read(sData, file.GetFileSize()); + + if (iGot == -1) + { + LOG->Warn("GetFileContents(%s): %s", sPath.c_str(), file.GetError().c_str()); + return false; + } + + if (bOneLine) + StripCrnl(sData); + + sOut = sData; + return true; +} + bool GetFileContents( const RString &sFile, vector &asOut ) { RageFile file; @@ -1387,6 +1475,21 @@ bool GetFileContents( const RString &sFile, vector &asOut ) return true; } +bool GetFileContents(const std::string &sFile, vector &asOut) +{ + RageFile file; + if (!file.Open(sFile)) + { + LOG->Warn("GetFileContents(%s): %s", sFile.c_str(), file.GetError().c_str()); + return false; + } + + RString sLine; + while (file.GetLine(sLine)) + asOut.push_back(sLine); + return true; +} + #ifndef USE_SYSTEM_PCRE #include "../extern/pcre/pcre.h" #else diff --git a/src/RageUtil.h b/src/RageUtil.h index 8dc26d632e..91aa16fdf8 100644 --- a/src/RageUtil.h +++ b/src/RageUtil.h @@ -462,6 +462,9 @@ void split( const wstring &sSource, const wstring &sDelimitor, int &iBegin, int RString join( const RString &sDelimitor, const vector& sSource ); RString join( const RString &sDelimitor, vector::const_iterator begin, vector::const_iterator end ); +std::string join(const std::string &sDelimitor, const vector& sSource); +std::string join(const std::string &sDelimitor, vector::const_iterator begin, vector::const_iterator end); + // These methods escapes a string for saving in a .sm or .crs file RString SmEscape( const RString &sUnescaped ); RString SmEscape( const char *cUnescaped, int len ); @@ -542,6 +545,8 @@ void StripMacResourceForks( vector &vs ); // Removes files starting wit RString DerefRedir( const RString &sPath ); bool GetFileContents( const RString &sPath, RString &sOut, bool bOneLine = false ); bool GetFileContents( const RString &sFile, vector &asOut ); +bool GetFileContents(const std::string &sPath, std::string &sOut, bool bOneLine = false); +bool GetFileContents(const std::string & sFile, vector& asOut); class Regex { diff --git a/src/ScoreManager.h b/src/ScoreManager.h index 2905a16ad2..a98f872e5a 100644 --- a/src/ScoreManager.h +++ b/src/ScoreManager.h @@ -4,7 +4,7 @@ #include "Grade.h" #include "GameConstantsAndTypes.h" #include - +#include // Scores for a specific rate for a specific chart struct ScoresAtRate diff --git a/src/archutils/Win32/mapconv.cpp b/src/archutils/Win32/mapconv.cpp index d2711b328d..f1be9ad692 100644 --- a/src/archutils/Win32/mapconv.cpp +++ b/src/archutils/Win32/mapconv.cpp @@ -23,7 +23,7 @@ char fnambuf[MAX_FNAMBUF]; char *fnamptr = fnambuf; long segbuf[MAX_SEGMENTS][2]; -int segcnt=0; +int segcnt = 0; int seggrp[MAX_SEGMENTS]; long grpstart[MAX_GROUPS]; @@ -32,13 +32,13 @@ long codeseg_flags = 0; FILE *f, *fo; char *strtack(char *s, const char *t, const char *s_max) { - while(s < s_max && (*s = *t)) + while (s < s_max && (*s = *t)) ++s, ++t; if (s == s_max) - return NULL; + return nullptr; - return s+1; + return s + 1; } bool readline() { @@ -47,14 +47,14 @@ bool readline() { int l = strlen(line); - if (l>0 && line[l-1]=='\n') - line[l-1]=0; + if (l>0 && line[l - 1] == '\n') + line[l - 1] = 0; return true; } bool findline(const char *searchstr) { - while(readline()) { + while (readline()) { if (strstr(line, searchstr)) return true; } @@ -65,37 +65,37 @@ bool findline(const char *searchstr) { /////////////////////////////////////////////////////////////////////////// /* dbghelp UnDecorateSymbolName() doesn't handle anonymous namespaces, - * which look like "?A0x30dd143a". Remove "@?A0x????????"; we don't - * want to see "::" in crash dump output, anyway. */ -void RemoveAnonymousNamespaces( char *p ) +* which look like "?A0x30dd143a". Remove "@?A0x????????"; we don't +* want to see "::" in crash dump output, anyway. */ +void RemoveAnonymousNamespaces(char *p) { - while( p = strstr( p, "@?A" ) ) + while (p = strstr(p, "@?A")) { int skip = 0, i; - if( strlen(p) < 13 ) + if (strlen(p) < 13) break; - for( i = 5; i < 13; ++i ) - if( !isxdigit(p[i]) ) + for (i = 5; i < 13; ++i) + if (!isxdigit(p[i])) skip = 1; - if( p[3] != '0' || p[4] != 'x' ) + if (p[3] != '0' || p[4] != 'x') skip = 1; - if( skip ) + if (skip) { ++p; continue; } - memmove( p, p+13, strlen(p+13)+1 ); + memmove(p, p + 13, strlen(p + 13) + 1); } } void parsename(long rva, char *func_name) { - RemoveAnonymousNamespaces( func_name ); - - fnamptr = strtack(fnamptr, func_name, fnambuf+MAX_FNAMBUF); - if(!fnamptr) + RemoveAnonymousNamespaces(func_name); + + fnamptr = strtack(fnamptr, func_name, fnambuf + MAX_FNAMBUF); + if (!fnamptr) throw "Too many func names; increase MAX_FNAMBUF."; } @@ -106,7 +106,6 @@ struct RVASorter { }; int main(int argc, char **argv) { - int i; long load_addr; if (argc<3) { @@ -117,12 +116,12 @@ int main(int argc, char **argv) { // TODO: Choose a better default for the vdi file. int ver = 20151002; - if (!(f=fopen(argv[1], "r"))) { + if (!(f = fopen(argv[1], "r"))) { printf("can't open listing file \"%s\"\n", argv[1]); return 20; } - if (!(fo=fopen(argv[2], "wb"))) { + if (!(fo = fopen(argv[2], "wb"))) { printf("can't open output file \"%s\"\n", argv[2]); return 20; } @@ -132,23 +131,23 @@ int main(int argc, char **argv) { try { line[0] = 0; -// printf("Looking for segment list.\n"); + // printf("Looking for segment list.\n"); if (!findline("Start Length")) throw "can't find segment list"; -// printf("Reading in segment list.\n"); + // printf("Reading in segment list.\n"); - while(readline()) { + while (readline()) { long grp, start, len; - if (3!=sscanf(line, "%lx:%lx %lx", &grp, &start, &len)) + if (3 != sscanf(line, "%lx:%lx %lx", &grp, &start, &len)) break; - if (strstr(line+49, "CODE")) { -// printf("%04x:%08lx %08lx type code\n", grp, start, len); + if (strstr(line + 49, "CODE")) { + // printf("%04x:%08lx %08lx type code\n", grp, start, len); - codeseg_flags |= 1<::iterator itRVA = rvabuf.begin(), itRVAEnd = rvabuf.end(); std::vector rvaout; long firstrva = (*itRVA++).rva; long lastrva = firstrva; - for(; itRVA != itRVAEnd; ++itRVA) { + for (; itRVA != itRVAEnd; ++itRVA) { long rvadiff = (*itRVA).rva - lastrva; lastrva += rvadiff; - if (rvadiff & 0xF0000000) rvaout.push_back((char)(0x80 | ((rvadiff>>28) & 0x7F))); - if (rvadiff & 0xFFE00000) rvaout.push_back((char)(0x80 | ((rvadiff>>21) & 0x7F))); - if (rvadiff & 0xFFFFC000) rvaout.push_back((char)(0x80 | ((rvadiff>>14) & 0x7F))); - if (rvadiff & 0xFFFFFF80) rvaout.push_back((char)(0x80 | ((rvadiff>> 7) & 0x7F))); + if (rvadiff & 0xF0000000) rvaout.push_back((char)(0x80 | ((rvadiff >> 28) & 0x7F))); + if (rvadiff & 0xFFE00000) rvaout.push_back((char)(0x80 | ((rvadiff >> 21) & 0x7F))); + if (rvadiff & 0xFFFFC000) rvaout.push_back((char)(0x80 | ((rvadiff >> 14) & 0x7F))); + if (rvadiff & 0xFFFFFF80) rvaout.push_back((char)(0x80 | ((rvadiff >> 7) & 0x7F))); rvaout.push_back((char)(rvadiff & 0x7F)); } -// printf("%ld bytes\n", rvaout.size()); + // printf("%ld bytes\n", rvaout.size()); // dump data - static const char header[64]="symbolic debug information\r\n\x1A"; + static const char header[64] = "symbolic debug information\r\n\x1A"; fwrite(header, 64, 1, fo); @@ -281,14 +279,15 @@ int main(int argc, char **argv) { fwrite(&firstrva, 4, 1, fo); fwrite(&rvaout[0], rvaout.size(), 1, fo); fwrite(fnambuf, fnamptr - fnambuf, 1, fo); - fwrite(segbuf, segcnt*8, 1, fo); + fwrite(segbuf, segcnt * 8, 1, fo); // really all done if (fclose(fo)) throw "output file close failed"; - - } catch(const char *s) { + + } + catch (const char *s) { fprintf(stderr, "%s: %s\n", argv[1], s); } @@ -298,26 +297,26 @@ int main(int argc, char **argv) { } /* - * (c) 2002 Avery Lee - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons to - * whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF - * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS - * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT - * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS - * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ +* (c) 2002 Avery Lee +* All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, and/or sell copies of the Software, and to permit persons to +* whom the Software is furnished to do so, provided that the above +* copyright notice(s) and this permission notice appear in all copies of +* the Software and that both the above copyright notice(s) and this +* permission notice appear in supporting documentation. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS +* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT +* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +* PERFORMANCE OF THIS SOFTWARE. +*/ \ No newline at end of file