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