1 ///////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2011 by The Allacrost Project
3 //            Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 //                         All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software
7 // and you may modify it and/or redistribute it under the terms of this license.
8 // See http://www.gnu.org/copyleft/gpl.html for details.
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 /** ****************************************************************************
12 *** \file    script_read.cpp
13 *** \author  Daniel Steuernol - steu@allacrost.org,
14 *** \author  Tyler Olsen - roots@allacrost.org
15 *** \author  Yohann Ferreira, yohann ferreira orange fr
16 *** \brief   Source file for the ReadScriptDescriptor class.
17 *** ***************************************************************************/
18 
19 #include "script_read.h"
20 
21 #include "script.h"
22 
23 #include "utils/utils_files.h"
24 #include "utils/utils_strings.h"
25 
26 using namespace luabind;
27 
28 using namespace vt_utils;
29 using namespace vt_script::private_script;
30 
31 namespace vt_script
32 {
33 
~ReadScriptDescriptor()34 ReadScriptDescriptor::~ReadScriptDescriptor()
35 {
36     CloseFile();
37 }
38 
39 //-----------------------------------------------------------------------------
40 // File Access Functions
41 //-----------------------------------------------------------------------------
42 
OpenFile(const std::string & filename)43 bool ReadScriptDescriptor::OpenFile(const std::string &filename)
44 {
45     // check for file existence
46     if(!DoesFileExist(filename)) {
47         PRINT_ERROR << "Attempted to open unavailable file: "
48                     << filename << std::endl;
49         return false;
50     }
51 
52     // Increases the global stack size by 1 element. That is needed because the new thread will be pushed in the
53     // stack and we have to be sure there is enough space there.
54     if (lua_checkstack(ScriptManager->GetGlobalState(), 1) == 0) {
55         PRINT_ERROR << "Couldn't add stack space for script file: " << filename << std::endl;
56         return false;
57     }
58 
59     _lstack = lua_newthread(ScriptManager->GetGlobalState());
60 
61     // Attempt to load and execute the Lua file
62     if(luaL_loadfile(_lstack, filename.c_str()) != 0 || lua_pcall(_lstack, 0, 0, 0)) {
63         PRINT_ERROR << "Could not open script file: " << filename << ", error message:" << std::endl
64                     << lua_tostring(_lstack, private_script::STACK_TOP) << std::endl;
65         _access_mode = SCRIPT_CLOSED;
66         return false;
67     }
68 
69     _filename = filename;
70     _access_mode = SCRIPT_READ;
71     // If the file is already open, we don't add it twice.
72     ScriptManager->_AddOpenFile(this);
73 
74     return true;
75 }
76 
OpenFile()77 bool ReadScriptDescriptor::OpenFile()
78 {
79     if(_filename.empty()) {
80         PRINT_ERROR << "could not open file because of an invalid file name (empty string)" << std::endl;
81         return false;
82     }
83 
84     return OpenFile(_filename);
85 }
86 
CloseFile()87 void ReadScriptDescriptor::CloseFile()
88 {
89     if(!IsFileOpen())
90         return;
91 
92     // Probably not needed. Script errors should be printed immediately.
93     if(IsErrorDetected()) {
94         PRINT_WARNING
95                 << "the file " << _filename << " had the following error messages remaining:"
96                 << std::endl << _error_messages.str() << std::endl;
97     }
98 
99     _error_messages.clear();
100     _open_tables.clear();
101     _access_mode = SCRIPT_CLOSED;
102 
103     // We free the thread (coroutine) so it can be garbage collected, or the app will end in a memory overflow.
104     lua_State* global_state = ScriptManager->GetGlobalState();
105     for (int32_t i = 1, n = lua_gettop(global_state); i <= n; ++i) {
106         if ((lua_type(global_state, i) == LUA_TTHREAD) && (lua_tothread(global_state, i) == _lstack)) {
107             lua_remove(global_state, i);
108             break;
109         }
110     }
111     _lstack = nullptr;
112 
113     ScriptManager->_TriggerLuaGarbageCollector();
114 
115     ScriptManager->_RemoveOpenFile(this);
116 }
117 
118 //-----------------------------------------------------------------------------
119 // Existence Checking Functions
120 //-----------------------------------------------------------------------------
121 
_DoesDataExist(const std::string & key,int32_t type)122 bool ReadScriptDescriptor::_DoesDataExist(const std::string &key, int32_t type)
123 {
124     // Check whether the user is trying to read a global variable or one stored in a table
125     if(_open_tables.empty()) {  // Variable is a global
126         lua_getglobal(_lstack, key.c_str());
127         luabind::object o(luabind::from_stack(_lstack, private_script::STACK_TOP));
128         return _CheckDataType(type, o);
129     }
130 
131     else { // Variable is a member of a table
132         luabind::object o(luabind::from_stack(_lstack, private_script::STACK_TOP));
133         if(luabind::type(o) != LUA_TTABLE) {
134             _error_messages << "* _DoesDataExist() failed because the top of the stack was not "
135                             << "a table when trying to check for the table member: " << key << std::endl;
136             return false;
137         }
138 
139         luabind::object obj(o[key]);
140         return _CheckDataType(type, obj);
141     }
142 }
143 
_DoesDataExist(int32_t key,int32_t type)144 bool ReadScriptDescriptor::_DoesDataExist(int32_t key, int32_t type)
145 {
146     if(_open_tables.empty()) {
147         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because no tables were open when trying to "
148                                        << "examine the table member: " << key << std::endl;
149         return false;
150     }
151 
152     luabind::object o(luabind::from_stack(_lstack, private_script::STACK_TOP));
153     if(luabind::type(o) != LUA_TTABLE) {
154         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the top of the stack was not "
155                                        << "a table when trying to check for the table member: " << key << std::endl;
156         return false;
157     }
158 
159     luabind::object obj(o[key]);
160     return _CheckDataType(type, obj);
161 }
162 
_CheckDataType(int32_t type,luabind::object & obj_check)163 bool ReadScriptDescriptor::_CheckDataType(int32_t type, luabind::object &obj_check)
164 {
165     int32_t object_type = luabind::type(obj_check);
166 
167     if(obj_check.is_valid() == false)
168         return false;
169 
170     // When this type is passed to the function, we don't care what type the object is as long
171     // as it was seen to be something
172     if(type == LUA_TNIL)
173         return true;
174 
175     // Simple type comparison is all that is needed for all non-numeric types
176     if(type == object_type)
177         return true;
178 
179     // Because Lua only has a "number" type, we have to do perform a special cast
180     // to examine integer versus floating point types
181     if(object_type == LUA_TNUMBER) {
182         if(type == INTEGER_TYPE) {
183             try {
184                 luabind::object_cast<int32_t>(obj_check);
185                 return true;
186             } catch(...) {
187                 return false;
188             }
189         } else if(type == UINTEGER_TYPE) {
190             try {
191                 luabind::object_cast<uint32_t>(obj_check);
192                 return true;
193             } catch(...) {
194                 return false;
195             }
196         } else if(type == FLOAT_TYPE) {
197             try {
198                 luabind::object_cast<float>(obj_check);
199                 return true;
200             } catch(...) {
201                 return false;
202             }
203         } else {
204             return false;
205         }
206     }
207 
208     else {
209         return false;
210     }
211 }
212 
213 //-----------------------------------------------------------------------------
214 // Function Pointer Read Functions
215 //-----------------------------------------------------------------------------
216 
ReadFunctionPointer(const std::string & key)217 luabind::object ReadScriptDescriptor::ReadFunctionPointer(const std::string &key)
218 {
219     if(_open_tables.empty()) {  // The function should be in the global space
220         lua_getglobal(_lstack, key.c_str());
221 
222         luabind::object o(from_stack(_lstack, STACK_TOP));
223 
224         if(!o.is_valid()) {
225             IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because it was unable to access the function "
226                                            << "for the global key: " << key << std::endl;
227             return luabind::object();
228         }
229 
230         if(type(o) != LUA_TFUNCTION) {
231             IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the data retrieved was not a function "
232                                            << "for the global key: " << key << std::endl;
233             return luabind::object();
234         }
235 
236         return o;
237     }
238 
239     else { // The function should be an element of the most recently opened table
240         luabind::object o(from_stack(_lstack, STACK_TOP));
241         if(type(o) != LUA_TTABLE) {
242             IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the top of the stack was not a table "
243                                            << "for the table element key: " << key << std::endl;
244             return luabind::object();
245         }
246 
247         if(type(o[key]) != LUA_TFUNCTION) {
248             IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the data retrieved was not a function "
249                                            << "for the table element key: " << key << std::endl;
250             return luabind::object();
251         }
252 
253         return o[key];
254     }
255 }
256 
ReadFunctionPointer(int32_t key)257 luabind::object ReadScriptDescriptor::ReadFunctionPointer(int32_t key)
258 {
259     // Fucntion is always a table element for integer keys
260     luabind::object o(from_stack(_lstack, STACK_TOP));
261     if(type(o) != LUA_TTABLE) {
262         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the top of the stack was not a table "
263                                        << "for the table element key: " << key << std::endl;
264         return o;
265     }
266 
267     if(type(o[key]) != LUA_TFUNCTION) {
268         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the data retrieved was not a function "
269                                        << "for the table element key: " << key << std::endl;
270         return o;
271     }
272 
273     return o[key];
274 }
275 
276 //-----------------------------------------------------------------------------
277 // Table Operation Functions
278 //-----------------------------------------------------------------------------
279 
OpenTable(const std::string & table_name,bool use_global)280 bool ReadScriptDescriptor::OpenTable(const std::string &table_name, bool use_global)
281 {
282     if(_open_tables.empty() || use_global) {  // Fetch the table from the global space
283         lua_getglobal(_lstack, table_name.c_str());
284         if(!lua_istable(_lstack, STACK_TOP)) {
285             IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the data retrieved was not a table "
286                                            << "or did not exist for the global key " << table_name << std::endl;
287             return false;
288         }
289         _open_tables.push_back(table_name);
290     }
291 
292     else { // The table to fetch is an element of another table
293         if (!DoesTableExist(table_name))
294             return false;
295 
296         lua_pushstring(_lstack, table_name.c_str());
297         lua_gettable(_lstack, STACK_TOP - 1);
298         if(!lua_istable(_lstack, STACK_TOP)) {
299             IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the data retrieved was not a table "
300                                            << "or did not exist for the table element key " << table_name << std::endl;
301             return false;
302         }
303         _open_tables.push_back(table_name);
304     }
305     return true;
306 }
307 
OpenTable(int32_t table_name)308 bool ReadScriptDescriptor::OpenTable(int32_t table_name)
309 {
310     // At least one table must be open to use a numerical key
311     if(_open_tables.empty()) {
312         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because there were no tables open when trying "
313                                        << "to open the with the element key " << table_name << std::endl;
314         return false;
315     }
316 
317     if (!DoesTableExist(table_name))
318         return false;
319 
320     lua_pushnumber(_lstack, table_name);
321 
322     if(!lua_istable(_lstack, STACK_TOP - 1)) {
323         IF_PRINT_WARNING(SCRIPT_DEBUG) << "about to fail because STACK_TOP - 1 is not a "
324                                        << "table, or the table does not exist for the table element key: " << table_name << std::endl;
325         return false;
326     }
327 
328     // Note: This call is unsafe and might make the game crash.
329     lua_gettable(_lstack, STACK_TOP - 1);
330 
331     if(!lua_istable(_lstack, STACK_TOP)) {
332         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the data retrieved was not a table "
333                                        << "or did not exist for the table element key " << table_name << std::endl;
334         return false;
335     }
336 
337     _open_tables.push_back(NumberToString(table_name));
338     return true;
339 }
340 
OpenTablespace()341 std::string ReadScriptDescriptor::OpenTablespace()
342 {
343     if(!IsFileOpen()) {
344         PRINT_ERROR << "Can't open a table space without opening a script file." << std::endl;
345         return std::string();
346     }
347 
348     std::string tablespace = GetTableSpace();
349 
350     if(tablespace.empty()) {
351         PRINT_ERROR << "The script filename is not valid to be used as tablespace name: " << _filename << std::endl;
352         return std::string();
353     }
354 
355     if (OpenTable(tablespace, true)) // Open the tablespace from the global stack.
356         return tablespace;
357     else
358         return std::string();
359 }
360 
CloseTable()361 void ReadScriptDescriptor::CloseTable()
362 {
363     if(_open_tables.empty()) {
364         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because there were no open tables to close" << std::endl;
365         return;
366     }
367 
368     _open_tables.pop_back();
369     lua_pop(_lstack, 1);
370 }
371 
CloseAllTables()372 void ReadScriptDescriptor::CloseAllTables()
373 {
374     while(_open_tables.size() != 0) {
375         CloseTable();
376     }
377 }
378 
GetTableSize(const std::string & table_name)379 uint32_t ReadScriptDescriptor::GetTableSize(const std::string &table_name)
380 {
381     uint32_t size = 0;
382 
383     if (OpenTable(table_name)) {
384         size = GetTableSize();
385         CloseTable();
386     }
387     return size;
388 }
389 
ClearStack(uint32_t levels_to_clear)390 void ReadScriptDescriptor::ClearStack(uint32_t levels_to_clear)
391 {
392     _open_tables.clear();
393     for(uint32_t i = 0; i < levels_to_clear; ++i)
394         lua_remove(_lstack, 0);
395 }
396 
GetTableSize(int32_t table_name)397 uint32_t ReadScriptDescriptor::GetTableSize(int32_t table_name)
398 {
399     uint32_t size = 0;
400 
401     if (OpenTable(table_name)) {
402         size = GetTableSize();
403         CloseTable();
404     }
405 
406     return size;
407 }
408 
GetTableSize()409 uint32_t ReadScriptDescriptor::GetTableSize()
410 {
411     if(_open_tables.empty()) {
412         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because there were no open tables to get the size of" << std::endl;
413         return 0;
414     }
415 
416     // lua returns 0 on table sizes in a couple situations
417     // 1. the indexes don't start from what lua expects
418     // 2. a hash table instead of an array table.
419     // So we'll just count the table size ourselves
420     object o(from_stack(_lstack, STACK_TOP));
421 
422     if(type(o) != LUA_TTABLE) {
423         IF_PRINT_WARNING(SCRIPT_DEBUG) << "failed because the top of the stack is not a table." << std::endl;
424         return 0;
425     }
426 
427     uint32_t table_size = 0;
428     for(luabind::iterator i(o); i != private_script::TABLE_END; ++i)
429         table_size++;
430 
431     return table_size;
432 }
433 
RunScriptFunction(const std::string & filename,const std::string & function_name,bool global)434 bool ReadScriptDescriptor::RunScriptFunction(const std::string &filename,
435         const std::string &function_name,
436         bool global)
437 {
438     // The error message handling is done into OpenFile()
439     if(!OpenFile(filename))
440         return false;
441 
442     if(!DoesFunctionExist(function_name)) {
443         PRINT_ERROR << "No '" << function_name << "' function!" << std::endl;
444         CloseFile();
445         return false;
446     }
447 
448     bool ran = false;
449     if(global || !OpenTablespace().empty())
450         ran = RunScriptFunction(function_name);
451 
452     CloseFile();
453 
454     return ran;
455 }
456 
RunScriptFunction(const std::string & function_name)457 bool ReadScriptDescriptor::RunScriptFunction(const std::string& function_name)
458 {
459     if(!IsFileOpen()) {
460         PRINT_ERROR << "Can't call function " << function_name << "without opening a script file." << std::endl;
461         return false;
462     }
463 
464     try {
465         luabind::call_function<void>(GetLuaState(), function_name.c_str());
466     } catch(const luabind::error &e) {
467         PRINT_ERROR << "Error while loading: " << function_name << std::endl;
468         ScriptManager->HandleLuaError(e);
469         return false;
470     } catch(const luabind::cast_failed &e) {
471         PRINT_ERROR << "Error while loading: " << function_name << std::endl;
472         ScriptManager->HandleCastError(e);
473         return false;
474     }
475 
476     return true;
477 }
478 
RunScriptObject(const luabind::object & object)479 bool ReadScriptDescriptor::RunScriptObject(const luabind::object &object)
480 {
481 
482     // Don't log in that case because we might want to run invalid (empty) objects
483     // to simplify the caller code.
484     if(!object.is_valid())
485         return true;
486 
487     try {
488         luabind::call_function<void>(object);
489     } catch(const luabind::error &e) {
490         PRINT_ERROR << "Error while loading script object." << std::endl;
491         ScriptManager->HandleLuaError(e);
492         return false;
493     } catch(const luabind::cast_failed &e) {
494         PRINT_ERROR << "Error while loading script object." << std::endl;
495         ScriptManager->HandleCastError(e);
496     }
497     return true;
498 }
499 
500 } // namespace vt_script
501