From 7d795e5703a406d01b53b9a4b8c9849fa9bd22c7 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Mon, 9 Oct 2006 05:51:12 +0000 Subject: [PATCH] first-pass: actor descriptions in Lua --- stepmania/src/ActorUtil.cpp | 52 +++++++++++++++++ stepmania/src/ActorUtil.h | 1 + stepmania/src/XmlFileUtil.cpp | 107 +++++++++++++++++++++++++++++++++- stepmania/src/XmlFileUtil.h | 2 + 4 files changed, 161 insertions(+), 1 deletion(-) diff --git a/stepmania/src/ActorUtil.cpp b/stepmania/src/ActorUtil.cpp index 0d45eea3d4..736f161923 100644 --- a/stepmania/src/ActorUtil.cpp +++ b/stepmania/src/ActorUtil.cpp @@ -314,6 +314,33 @@ static void MergeActorXML( XNode *pChild, const XNode *pParent ) } } +namespace +{ + XNode *LoadXNodeFromLuaShowErrors( const RString &sFile ) + { + RString sScript; + if( !GetFileContents(sFile, sScript) ) + return NULL; + + Lua *L = LUA->Get(); + + RString sError; + if( !LuaHelpers::RunScript(L, sScript, sFile, sError, 1) ) + { + lua_pop( L, 1 ); + LUA->Release( L ); + sError = ssprintf( "Lua runtime error: %s", sError.c_str() ); + Dialog::OK( sError, "LUA_ERROR" ); + return NULL; + } + + XNode *pRet = XmlFileUtil::XNodeFromTable( L ); + + LUA->Release( L ); + return pRet; + } +} + /* * If pParent is non-NULL, it's the parent node when nesting XML, which is * used only by ActorUtil::LoadFromNode. @@ -344,6 +371,16 @@ Actor* ActorUtil::MakeActor( const RString &sPath_, const XNode *pParent, Actor } } + if( ft == FT_Directory ) + { + RString sLuaPath = sPath + "default.lua"; + if( DoesFileExist(sLuaPath) ) + { + sPath = sLuaPath; + ft = FT_Lua; + } + } + RString sDir = Dirname( sPath ); switch( ft ) { @@ -367,6 +404,19 @@ Actor* ActorUtil::MakeActor( const RString &sPath_, const XNode *pParent, Actor MergeActorXML( &xml, pParent ); return ActorUtil::LoadFromNode( sDir, &xml ); } + case FT_Lua: + { + auto_ptr pNode( LoadXNodeFromLuaShowErrors(sPath) ); + if( pNode.get() == NULL ) + { + // XNode will warn about the error + return new Actor; + } + + MergeActorXML( pNode.get(), pParent ); + Actor *pRet = ActorUtil::LoadFromNode( sDir, pNode.get() ); + return pRet; + } case FT_Bitmap: case FT_Movie: { @@ -503,6 +553,7 @@ static const char *FileTypeNames[] = { "Movie", "Directory", "Xml", + "Lua", "Model", }; XToString( FileType, NUM_FileType ); @@ -513,6 +564,7 @@ FileType ActorUtil::GetFileType( const RString &sPath ) sExt.MakeLower(); if( sExt=="xml" ) return FT_Xml; + else if( sExt=="lua" ) return FT_Lua; else if( sExt=="png" || sExt=="jpg" || diff --git a/stepmania/src/ActorUtil.h b/stepmania/src/ActorUtil.h index 0d2e4730a5..81f0bb48de 100644 --- a/stepmania/src/ActorUtil.h +++ b/stepmania/src/ActorUtil.h @@ -33,6 +33,7 @@ enum FileType FT_Movie, FT_Directory, FT_Xml, + FT_Lua, FT_Model, NUM_FileType, FT_Invalid diff --git a/stepmania/src/XmlFileUtil.cpp b/stepmania/src/XmlFileUtil.cpp index 199471bb1b..a1286e9122 100644 --- a/stepmania/src/XmlFileUtil.cpp +++ b/stepmania/src/XmlFileUtil.cpp @@ -640,8 +640,113 @@ void XmlFileUtil::CompileXNodeTree( XNode *pNode, const RString &sFile ) LUA->Release( L ); } +namespace +{ + XNode *XNodeFromTableRecursive( lua_State *L, const RString &sName, LuaReference &ProcessedTables ) + { + XNode *pNode = new XNode( sName ); + + /* Set the value of the node to the table. */ + { + XNodeLuaValue *pValue = new XNodeLuaValue; + lua_pushvalue( L, -1 ); + pValue->SetValueFromStack( L ); + pNode->SetValueFrom( pValue ); + } + + /* Iterate over the table, pulling out attributes and tables to process. */ + map NodesToAdd; + lua_pushnil( L ); + while( lua_next(L, -2) ) + { + lua_pushvalue( L, -2 ); + RString sName; + LuaHelpers::Pop( L, sName ); + + /* If this entry is a table, add it recursively. */ + if( lua_istable(L, -1) ) + { + NodesToAdd[sName].SetFromStack( L ); + continue; + } + + if( lua_isstring(L, -1) && EndsWith(sName, "Command") ) + { + RString sExpression; + LuaHelpers::Pop( L, sExpression ); + LuaHelpers::ParseCommandList( L, sExpression, "" /* XXX */ ); + } + + /* Otherwise, add an attribute. */ + XNodeLuaValue *pValue = new XNodeLuaValue; + pValue->SetValueFromStack( L ); + pNode->AppendAttrFrom( sName, pValue ); + } + lua_pop( L, 1 ); // pop nil + + /* Recursively process tables. */ + FOREACHM( RString, LuaReference, NodesToAdd, t ) + { + const RString &sNodeName = t->first; + LuaReference &NodeToAdd = t->second; + + /* Check if the table is on the stack. */ + ProcessedTables.PushSelf( L ); + NodeToAdd.PushSelf( L ); // push table + lua_gettable( L, -2 ); + + bool bSawThisTableAlready = !lua_isnil(L, -1); + lua_pop( L, 2 ); // pop lua_gettable result, ProcessedTables + if( bSawThisTableAlready ) + continue; + + /* Add the table to the stack. */ + ProcessedTables.PushSelf( L ); + NodeToAdd.PushSelf( L ); + lua_pushboolean( L, true ); + lua_settable( L, -3 ); + lua_pop( L, 1 ); // pop ProcessedTables + + NodeToAdd.PushSelf( L ); + XNode *pNewNode = XNodeFromTableRecursive( L, sNodeName, ProcessedTables ); + if( pNewNode ) + pNode->AppendChild( pNewNode ); + + /* Remove the table from the stack. */ + ProcessedTables.PushSelf( L ); + NodeToAdd.PushSelf( L ); + lua_pushnil( L ); + lua_settable( L, -3 ); + lua_pop( L, 1 ); // pop ProcessedTables + } + + return pNode; + } + +} + /* - * (c) 2001-2004 Chris Danford + * Pop a table off of the stack, and return an XNode tree referring recursively + * to entries in the table. + * + * The table may not contain table cycles; if a cycle is detected, only the first + * table seen will have a corresponding XNode. + * + * Users of the resulting XNode may access the original table via PushValue. + */ +XNode *XmlFileUtil::XNodeFromTable( lua_State *L ) +{ + /* Maintain a set of references that we've created. Tables may loop; XNode trees may + * not. If we encounter a cycle, skip creating an XNode for that node. */ + LuaReference ProcessedTables; + lua_newtable( L ); + ProcessedTables.SetFromStack( L ); + + return XNodeFromTableRecursive( L, "Layer", ProcessedTables ); +} + +/* + * (c) 2001-2006 Chris Danford, Glenn Maynard * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a diff --git a/stepmania/src/XmlFileUtil.h b/stepmania/src/XmlFileUtil.h index 72f2e7e3b2..b10f71bf0d 100644 --- a/stepmania/src/XmlFileUtil.h +++ b/stepmania/src/XmlFileUtil.h @@ -5,6 +5,7 @@ class RageFileBasic; class XNode; +struct lua_State; namespace XmlFileUtil { @@ -19,6 +20,7 @@ namespace XmlFileUtil bool SaveToFile( const XNode *pNode, RageFileBasic &f, const RString &sStylesheet = "", bool bWriteTabs = true ); void CompileXNodeTree( XNode *pNode, const RString &sFile ); + XNode *XNodeFromTable( lua_State *L ); } #endif