///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004-2007 by The Allacrost Project
// All Rights Reserved
//
// This code is licensed under the GNU GPL version 2. It is free software
// and you may modify it and/or redistribute it under the terms of this license.
// See http://www.gnu.org/copyleft/gpl.html for details.
///////////////////////////////////////////////////////////////////////////////
/** ****************************************************************************
*** \file script_read.h
*** \author Daniel Steuernol - steu@allacrost.org,
*** Tyler Olsen - roots@allacrost.org
*** \brief Header file for the ReadScriptDescriptor class.
*** ***************************************************************************/
#ifndef __SCRIPT_READ_HEADER__
#define __SCRIPT_READ_HEADER__
#include "utils.h"
#include "defs.h"
#include "script.h"
namespace hoa_script {
namespace private_script {
/** \brief Constants used in the ReadScriptDescriptor data existence checking functions
*** These members are necessary because the Lua type constants defined do not distinguish
*** between integers and floats (there is only a single LUA_TNUMBER). We, however, need
*** to sometimes distinguish between the two types when reading in data. These constants
*** were assigned arbitrary and unlikely values to be represented by other Lua types
*** (which as of Lua 5.0, go from -1 to 8).
**/
//@{
const int32 INTEGER_TYPE = 0x12345678;
const int32 UINTEGER_TYPE = 0x87654321;
const int32 FLOAT_TYPE = 0x12344321;
//@}
} // namespace private_script
/** ****************************************************************************
*** \brief Represents a Lua file opened with read and execute permissions
***
*** This is the most commonly used of the script descriptor classes. This class
*** allows you to read simple data types, open and close tables contained within
*** the file, check for the existence of specific variable names and identifiers,
*** retrieve pointers to Lua functions which can then be executed, and more.
***
*** The most important concept for understanding how to use this class is that of
*** table scope. A scope is the portion of the Lua file that many of the
*** data read operations in this class operate on. For example, the global scope
*** contains all global variables, functions, and tables. There is a unique scope
*** inherit inside each table in the file (including the tables of tables). When
*** you invoke a read operation, ReadInt() for example, it attempts to read an
*** integer in the current scope. If you have no tables open, it will look in the
*** global space. Otherwise, it will look in the most recently opened table. Thus,
*** it is extremely important that you know which scope is "active", and
*** to know when to open and close tables to be in the correct scope.
***
*** \note With the exception of the open/close file functions, all methods of
*** this class assume that the file is open and do not check otherwise. If you
*** try to invoke these functions with an unopened file, you will generate a
*** segmentation fault.
*** ***************************************************************************/
class ReadScriptDescriptor : public ScriptDescriptor {
friend class GameScript;
public:
virtual ~ReadScriptDescriptor();
/** \name File Access Functions
*** \note These are derived from ScriptDescriptor, refer to the comments for these
*** methods in the header file for that class.
**/
//@{
virtual bool OpenFile(const std::string& file_name);
virtual bool OpenFile(const std::string& file_name, bool force_reload);
virtual bool OpenFile();
virtual void CloseFile();
//@}
/** \name Existence Checking Functions
*** \brief Methods which check if there exist certain data names and types in a script file
*** \param key The variable, table, or function name to check for
*** \return True if the appropriate data type represented by that variable name was found.
***
*** Under normal circumstances, invoking any of these functions should not result in error
*** messages being printed, even if the variable could not be found. There are some
*** circumstances, however, where error messages may be added, such as when it is detected
*** that a table is missing or the active scope is not appropriate for the function call.
***
*** \note The DoesVariableExist checks if is there is any data type, structure, or
*** function referenced by the key name.
**/
//@{
bool DoesVariableExist(const std::string& key)
{ return _DoesDataExist(key, LUA_TNIL); }
bool DoesVariableExist(int32 key)
{ return _DoesDataExist(key, LUA_TNIL); }
bool DoesBoolExist(const std::string& key)
{ return _DoesDataExist(key, LUA_TBOOLEAN); }
bool DoesBoolExist(int32 key)
{ return _DoesDataExist(key, LUA_TBOOLEAN); }
bool DoesIntExist(const std::string& key)
{ return _DoesDataExist(key, private_script::INTEGER_TYPE); }
bool DoesIntExist(int32 key)
{ return _DoesDataExist(key, private_script::INTEGER_TYPE); }
bool DoesUIntExist(const std::string& key)
{ return _DoesDataExist(key, private_script::UINTEGER_TYPE); }
bool DoesUIntExist(int32 key)
{ return _DoesDataExist(key, private_script::UINTEGER_TYPE); }
bool DoesFloatExist(const std::string& key)
{ return _DoesDataExist(key, private_script::FLOAT_TYPE); }
bool DoesFloatExist(int32 key)
{ return _DoesDataExist(key, private_script::FLOAT_TYPE); }
bool DoesStringExist(const std::string& key)
{ return _DoesDataExist(key, LUA_TSTRING); }
bool DoesStringExist(int32 key)
{ return _DoesDataExist(key, LUA_TSTRING); }
bool DoesFunctionExist(const std::string& key)
{ return _DoesDataExist(key, LUA_TFUNCTION); }
bool DoesFunctionExist(int32 key)
{ return _DoesDataExist(key, LUA_TFUNCTION); }
bool DoesTableExist(const std::string& key)
{ return _DoesDataExist(key, LUA_TTABLE); }
bool DoesTableExist(int32 key)
{ return _DoesDataExist(key, LUA_TTABLE); }
//@}
/** \name Variable Read Functions
*** \brief These functions grab a basic data type from the Lua file and return its value.
*** \param key The name or numeric id of the Lua variable to access.
*** \return The value of the variable requested.
*** \note The integer keys are only valid for variables stored in a table, not for global variables.
***
*** These functions call the template _ReadData() functions with a default return value.
**/
//@{
bool ReadBool(const std::string& key)
{ return _ReadData(key, false); }
bool ReadBool(int32 key)
{ return _ReadData(key, false); }
int32 ReadInt(const std::string& key)
{ return _ReadData(key, 0); }
int32 ReadInt(int32 key)
{ return _ReadData(key, 0); }
uint32 ReadUInt(const std::string& key)
{ return _ReadData(key, 0); }
uint32 ReadUInt(int32 key)
{ return _ReadData(key, 0); }
float ReadFloat(const std::string& key)
{ return _ReadData(key, 0.0f); }
float ReadFloat(int32 key)
{ return _ReadData(key, 0.0f); }
std::string ReadString(const std::string& key)
{ return _ReadData(key, ""); }
std::string ReadString(int32 key)
{ return _ReadData(key, ""); }
hoa_utils::ustring ReadUString(const std::string& key)
{ return _ReadData(key, hoa_utils::MakeUnicodeString("")); }
hoa_utils::ustring ReadUString(int32 key)
{ return _ReadData(key, hoa_utils::MakeUnicodeString("")); }
//@}
/** \name Vector Read Functions
*** \brief These functions fill a vector with members of a table read from the Lua file.
*** \param key The name of the table to use to fill the vector.
*** \param vect A reference to the vector of elements to fill.
***
*** The table that these functions attempt to access is assumed to be closed. If the
*** table is open prior to calling these functions, they will not operate properly. All of
*** these functions call the template _ReadDataVector() functions to perform their operations.
***
*** \note The integer keys are only valid for tables that are elements of a parent table.
*** They can not be used to access tables in the global space.
**/
//@{
void ReadBoolVector(const std::string& key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadBoolVector(int32 key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadIntVector(const std::string& key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadIntVector(int32 key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadUIntVector(const std::string& key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadUIntVector(int32 key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadFloatVector(const std::string& key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadFloatVector(int32 key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadStringVector(const std::string& key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadStringVector(int32 key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadUStringVector(const std::string& key, std::vector& vect)
{ _ReadDataVector(key, vect); }
void ReadUStringVector(int32 key, std::vector& vect)
{ _ReadDataVector(key, vect); }
//@}
/** \name Function Pointer Read Functions
*** \param key The name of the function if it is contained in the global space, or the key
*** if the function is embedded in a table.
*** \return A luabind::object class object, which can be used to call the function. It effectively
*** serves as a function pointer.
**/
//@{
luabind::object ReadFunctionPointer(const std::string& key);
//! \note The calling function may not be contained within the global space for an integer key.
luabind::object ReadFunctionPointer(int32 key);
//@}
/** \name Table Operation Functions
*** After a table is opened, it becomes the active "space" that all of the data read
*** operations operate on. You must always remember to close a table once you are
*** finished reading data from it.
**/
//@{
//! \param table_name The name of the table to open
//! \param use_global This overrides the open_tables vector, the reason for this is
//! when a function is called from lua, any open tables are no longer on the stack passed to the function
//! so to start a new chain of open tables we have to ignore the open tables vector
void OpenTable(const std::string& table_name, bool use_global = false);
/** \param table_name The integer key of the table to open
*** \note This function will only work when there is at least one other table already open
**/
void OpenTable(int32 table_name);
//! \brief Closes the most recently opened table
void CloseTable();
//! \brief Closes any open tables and returns the user to the global scope
void CloseAllTables();
/** \brief Returns the number of elements stored in an un-opened table
*** \param table_name The name of the (un-open) table to get the size of
**/
uint32 GetTableSize(const std::string& table_name);
/** \brief Returns the number of elements stored in an un-opened table
*** \param table_name The integer key of the (un-open) table to get the size of
*** \note This function will only work when there is at least one other table already open
**/
uint32 GetTableSize(int32 table_name);
//! \brief Returns the number of elements stored in the most recently opened table
uint32 GetTableSize();
//! \brief Emptys all values off the lua stack
void ClearStack(uint32 levels_to_clear);
/** \brief Fills a vector with all of the keys of a table
*** \param table_name The name of the table to open and retrieve the keys from
*** \param keys A reference to the vector where to store the table keys
***
*** The functions without a table_name argument will retrieve the keys for the
*** most recently opened table. The keys vector will cleared before the function
*** starts populating it with table keys, so make sure nothing important is contained
*** in the keys vector before calling this function. If the keys vector is empty
*** after the function was called, either an error occured or the table was empty.
***
*** \note These functions will only work successfully for tables that have the same
*** data type for all their keys (ie, all string keys or all integer keys). A table
*** with mixed key types (integers and strings for example) will not be processed
*** successfully by these functions.
**/
//@{
void ReadTableKeys(std::vector& keys)
{ _ReadTableKeys(keys); }
void ReadTableKeys(std::vector& keys)
{ _ReadTableKeys(keys); }
void ReadTableKeys(std::vector& keys)
{ _ReadTableKeys(keys); }
void ReadTableKeys(const std::string& table_name, std::vector& keys)
{ OpenTable(table_name); _ReadTableKeys(keys); CloseTable(); }
void ReadTableKeys(const std::string& table_name, std::vector& keys)
{ OpenTable(table_name); _ReadTableKeys(keys); CloseTable(); }
void ReadTableKeys(const std::string& table_name, std::vector& keys)
{ OpenTable(table_name); _ReadTableKeys(keys); CloseTable(); }
void ReadTableKeys(int32 table_name, std::vector& keys)
{ OpenTable(table_name); _ReadTableKeys(keys); CloseTable(); }
void ReadTableKeys(int32 table_name, std::vector& keys)
{ OpenTable(table_name); _ReadTableKeys(keys); CloseTable(); }
void ReadTableKeys(int32 table_name, std::vector& keys)
{ OpenTable(table_name); _ReadTableKeys(keys); CloseTable(); }
//@}
//@}
//! \brief Returns a pointer to the local lua state (use with caution)
lua_State* GetLuaState()
{ return _lstack; }
/** \brief Prints out the contents of the Lua stack mechanism to standard output
*** The elements are printed from stack top to stack bottom.
**/
void DEBUG_PrintLuaStack();
//! \brief Prints out all global variable names to standard output
void DEBUG_PrintGlobals();
//! \brief Prints out a specific table
void DEBUG_PrintTable(luabind::object table, int tab = 0);
protected:
//! \brief The Lua stack, which handles all data sharing between C++ and Lua.
lua_State *_lstack;
/** \name Data Existence Check Functions
*** \brief These functions are called by the public DoesTYPEExist functions of this class.
*** \param key The name or numeric id of the Lua data to check.
*** \param type The correct type that the data should check out to be (uses the Lua type constants)
*** \return True if data of the corresponding key and type is found.
*** \note The integer keys are only valid for variables stored in a table, not for global variables.
**/
//@{
bool _DoesDataExist(const std::string& key, int32 type);
bool _DoesDataExist(int32 key, int32 type);
/** \brief A helper function for the _DoesDataExist functions that performs the data type check
*** \param type An integer type to compare with the type of the object
*** \param obj_check The Luabind object whose type to compare to the integer type
*** \return True if the two types are equivalent
**/
bool _CheckDataType(int32 type, luabind::object& obj_check);
//@}
/** \name Variable Read Templates
*** \brief These template functions are called by the public ReadTYPE functions of this class.
*** \param key The name or numeric identifier of the Lua variable to access.
*** \param default_value The value for the function to return if an error occurs.
*** \return The value of the variable requested.
*** \note Integer keys are only valid for variables stored in a table, not for global variables.
**/
//@{
template T _ReadData(const std::string& key, T default_value);
template T _ReadData(int32 key, T default_value);
//@}
/** \name Vector Read Templates
*** \brief These template functions are called by the public ReadTYPEVector functions of this class.
*** \param key The name or numeric identifier of the Lua variable to access.
*** \param vect A reference to the vector where the read variables should be stored
*** \note Integer keys are only valid for variables stored in a table, not for global variables.
**/
//@{
template void _ReadDataVector(const std::string& key, std::vector& vect);
template void _ReadDataVector(int32 key, std::vector& vect);
//! \brief This template method is a helper function for the other two
template void _ReadDataVectorHelper(std::vector& vect);
//@}
/** \name Table Key Template
*** \brief This template function fills a vector with all of the keys contained by the table
*** \param vect A reference to the vector where the keys should be stored
*** \note This function will fail for any table that has multiple key types (ie, contains both
*** integer and string keys).
**/
template void _ReadTableKeys(std::vector& keys);
}; // class ReadScriptDescriptor
//-----------------------------------------------------------------------------
// Template Function Definitions
//-----------------------------------------------------------------------------
template T ReadScriptDescriptor::_ReadData(const std::string &key, T default_value) {
// Check whether the user is trying to read a global variable or one stored in a table
if (_open_tables.size() == 0) { // Variable is a global
lua_getglobal(_lstack, key.c_str());
luabind::object o(luabind::from_stack(_lstack, private_script::STACK_TOP));
if (!o) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "unable to access the global variable: " << key
<< " Type: " << luabind::type(o) << std::endl;
return default_value;
}
try {
T ret_val = luabind::object_cast(o);
lua_pop(_lstack, 1);
return ret_val;
}
catch (...) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "unable to cast value to correct type for global variable: " << key << std::endl;
return default_value;
}
}
else { // Variable is a member of a table
luabind::object o(luabind::from_stack(_lstack, private_script::STACK_TOP));
if (luabind::type(o) != LUA_TTABLE) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the top of the stack was not a table when trying to read variable: " << key
<< " Type: " << luabind::type(o) << std::endl;
return default_value;
}
try {
return luabind::object_cast(o[key]);
}
catch (...) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "unable to access the table variable: " << key << std::endl;
return default_value;
}
}
return default_value;
} // template T ReadScriptDescriptor::_ReadData(const char *key, T default_value)
template T ReadScriptDescriptor::_ReadData(int32 key, T default_value) {
if (_open_tables.size() == 0) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because no tables were open when trying to access the table variable: "
<< key << std::endl;
return default_value;
}
luabind::object o(luabind::from_stack(_lstack, private_script::STACK_TOP));
if (luabind::type(o) != LUA_TTABLE) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the top of the stack was not a table when trying to read variable: "
<< key << std::endl;
return default_value;
}
try {
return luabind::object_cast(o[key]);
}
catch (...) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "unable to access the table variable: " << key << std::endl;
return default_value;
}
return default_value;
} // template T ReadScriptDescriptor::_ReadData(int32 key, T default_value)
template void ReadScriptDescriptor::_ReadDataVector(const std::string& key, std::vector& vect) {
// Open the table and grab if off the stack
OpenTable(key);
_ReadDataVectorHelper(vect);
CloseTable();
} // template void ReadScriptDescriptor::_ReadDataVector(std::string key, std::vector &vect)
template void ReadScriptDescriptor::_ReadDataVector(int32 key, std::vector& vect) {
if (_open_tables.size() == 0) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because no tables were open when trying to access the table variable: "
<< key << std::endl;;
return;
}
// Open the table and grab if off the stack
OpenTable(key);
_ReadDataVectorHelper(vect);
CloseTable();
} // template void ReadScriptDescriptor::_ReadDataVector(int32 key, std::vector &vect)
template void ReadScriptDescriptor::_ReadDataVectorHelper(std::vector& vect) {
luabind::object o(luabind::from_stack(_lstack, private_script::STACK_TOP));
if (luabind::type(o) != LUA_TTABLE) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the top of the stack was not a table" << std::endl;
return;
}
// Iterate through all the items of the table and place it in the vector
for (luabind::iterator it(o); it != private_script::TABLE_END; it++) {
try {
vect.push_back(luabind::object_cast((*it)));
}
catch (...) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed due to a type cast failure when reading the table" << std::endl;
}
}
} // template void ReadScriptDescriptor::_ReadDataVectorHelper(std::vector& vect)
template void ReadScriptDescriptor::_ReadTableKeys(std::vector& keys) {
keys.clear();
if (_open_tables.size() == 0) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because there were no open tables to get the keys of" << std::endl;
return;
}
luabind::object table(luabind::from_stack(_lstack, private_script::STACK_TOP));
if (luabind::type(table) != LUA_TTABLE) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the top of the stack was not a table" << std::endl;
return;
}
for (luabind::iterator i(table); i != private_script::TABLE_END; i++) {
try {
keys.push_back(luabind::object_cast(i.key()));
}
catch (...) {
IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed due to a type cast failure when retrieving a table key" << std::endl;
keys.clear();
return;
}
}
} // template void ReadScriptDescriptor::ReadTableKeys(std::vector& keys) {
} // namespace hoa_script
#endif // __SCRIPT_READ_HEADER__