1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2010 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 /** ****************************************************************************
11 *** \file script_modify.cpp
12 *** \author Daniel Steuernol - steu@allacrost.org,
13 *** Tyler Olsen - roots@allacrost.org
14 *** \brief Source file for the ModifyScriptDescriptor class.
15 *** ***************************************************************************/
16
17 #include "script.h"
18
19 #include "script_modify.h"
20 #include "script_read.h"
21 #include "script_write.h"
22
23 using namespace std;
24 using namespace luabind;
25
26 using namespace hoa_utils;
27
28 namespace hoa_script {
29
~ModifyScriptDescriptor()30 ModifyScriptDescriptor::~ModifyScriptDescriptor() {
31 if (IsFileOpen()) {
32 if (SCRIPT_DEBUG)
33 cerr << "SCRIPT WARNING: ModifyScriptDescriptor destructor was called when file was still open: "
34 << _filename << endl;
35 CloseFile();
36 }
37
38 _filename = "";
39 _access_mode = SCRIPT_CLOSED;
40 _error_messages.clear();
41 _open_tables.clear();
42 }
43
44 //-----------------------------------------------------------------------------
45 // File Access Functions
46 //-----------------------------------------------------------------------------
47
OpenFile(const std::string & file_name)48 bool ModifyScriptDescriptor::OpenFile(const std::string& file_name) {
49 if (ScriptManager->IsFileOpen(file_name) == true) {
50 if (SCRIPT_DEBUG)
51 cerr << "SCRIPT WARNING: ModifyScriptDescriptor::OpenFile() attempted to open file that is already opened: "
52 << file_name << endl;
53 return false;
54 }
55
56 // Check if this file was opened previously.
57 if ((this->_lstack = ScriptManager->_CheckForPreviousLuaState(file_name)) == NULL)
58 {
59 // Increases the global stack size by 1 element. That is needed because the new thread will be pushed in the
60 // stack and we have to be sure there is enough space there.
61 lua_checkstack(ScriptManager->GetGlobalState(),1);
62 _lstack = lua_newthread(ScriptManager->GetGlobalState());
63
64 // Attempt to load and execute the Lua file.
65 if (luaL_loadfile(_lstack, file_name.c_str()) != 0 || lua_pcall(_lstack, 0, 0, 0)) {
66 cerr << "SCRIPT ERROR: ModifyScriptDescriptor::OpenFile() could not open the file " << file_name << endl;
67 _access_mode = SCRIPT_CLOSED;
68 return false;
69 }
70 }
71
72 // Write out some global stuff
73 _filename = file_name;
74 _access_mode = SCRIPT_MODIFY;
75 ScriptManager->_AddOpenFile(this);
76 return true;
77 } // bool ModifyScriptDescriptor::OpenFile(std::string file_name)
78
79
80
OpenFile()81 bool ModifyScriptDescriptor::OpenFile() {
82 if (_filename == "") {
83 if (SCRIPT_DEBUG)
84 cerr << "SCRIPT ERROR: ModifyScriptDescriptor::OpenFile(), could not open file "
85 << "because of an invalid file name (empty string)." << endl;
86 return false;
87 }
88
89 return OpenFile(_filename);
90 }
91
92
93
CloseFile()94 void ModifyScriptDescriptor::CloseFile() {
95 if (IsFileOpen() == false) {
96 if (SCRIPT_DEBUG)
97 cerr << "SCRIPT ERROR: ModifyScriptDescriptor::CloseFile() could not close the "
98 << "file because it was not open." << endl;
99 return;
100 }
101
102 if (SCRIPT_DEBUG && IsErrorDetected()) {
103 cerr << "SCRIPT WARNING: In ModifyScriptDescriptor::CloseFile(), the file " << _filename
104 << " had error messages remaining. They are as follows:" << endl;
105 cerr << _error_messages.str() << endl;
106 }
107
108 _lstack = NULL;
109 _error_messages.clear();
110 _open_tables.clear();
111 _access_mode = SCRIPT_CLOSED;
112 ScriptManager->_RemoveOpenFile(this);
113 }
114
115
116 //-----------------------------------------------------------------------------
117 // Commit Function Definitions
118 //-----------------------------------------------------------------------------
119
CommitChanges(bool leave_closed)120 void ModifyScriptDescriptor::CommitChanges(bool leave_closed) {
121 WriteScriptDescriptor file; // The file to write the modified Lua state out to
122 string temp_filename = _filename.substr(0, _filename.find_last_of('.')) + "_TEMP" + _filename.substr(_filename.find_last_of('.'));
123
124 if (file.OpenFile(temp_filename) == false) {
125 if (SCRIPT_DEBUG)
126 _error_messages << "* ModifyScriptDescriptor::CommitChanges() failed because it could not open "
127 << "the file to write the modifications to" << endl;
128 return;
129 }
130
131 // setup the iterator
132 _open_tables_iterator = _open_tables.begin();
133
134 // Write the global tables to the file. This in turn will write all other tables that are members of
135 // the global tables, or members of those tables, and so on.
136 object globals(luabind::from_stack(_lstack, LUA_GLOBALSINDEX));
137 _CommitTable(file, globals);
138
139 file.CloseFile(); // Close the temporary file we were writing to
140 CloseFile(); // Close this file as well as it is about to be over-written
141
142 // Now overwrite this file with the temporary file written, remove the temporary file, and re-open the new file
143
144 if (MoveFile(temp_filename, _filename) == false) {
145 _error_messages << "* ModifyScriptDescriptor::CommitChanges() failed because after writing the temporary file "
146 << temp_filename << ", it could not be moved to overwrite the original filename " << _filename << endl;
147 }
148
149 if (leave_closed == false)
150 OpenFile();
151 } // void ModifyScriptDescriptor::CommitChanges(bool leave_closed)
152
153
154
_CommitTable(WriteScriptDescriptor & file,const luabind::object & table)155 void ModifyScriptDescriptor::_CommitTable(WriteScriptDescriptor& file, const luabind::object& table) {
156 bool key_is_numeric; // Set to true when a variable's key is not a string
157 int32 num_key = 0; // Holds the current numeric key
158 string str_key = ""; // Holds the current string key
159
160 for (luabind::iterator it(table), end; it != end; ++it) {
161 try {
162 num_key = object_cast<int32>(it.key());
163 key_is_numeric = true;
164 } catch (...) {
165 str_key = object_cast<string>(it.key());
166 key_is_numeric = false;
167 }
168
169 if (key_is_numeric && _open_tables_iterator == _open_tables.end())
170 {
171 cerr << "ModifyScriptDescriptor::_CommitTable: reached numeric key before writing out open tables" << endl;
172 return;
173 }
174 else if (!key_is_numeric && _open_tables_iterator != _open_tables.end())
175 {
176 if (str_key == (*_open_tables_iterator))
177 {
178 file.BeginTable(str_key);
179 _open_tables_iterator++;
180 _CommitTable(file, object(*it));
181 file.EndTable();
182 return;
183 }
184 continue;
185 }
186
187 // Check for _G table and do not write it out, causes
188 // infinite recursion.
189 if (!key_is_numeric)
190 if (str_key == "_G")
191 continue;
192 switch (luabind::type(*it)) {
193 case LUA_TBOOLEAN:
194 if (key_is_numeric)
195 file.WriteBool(num_key, object_cast<bool>(*it));
196 else
197 file.WriteBool(str_key, object_cast<bool>(*it));
198 break;
199 case LUA_TNUMBER:
200 try {
201 if (key_is_numeric)
202 file.WriteFloat(num_key, object_cast<float>(*it));
203 else
204 file.WriteFloat(str_key, object_cast<float>(*it));
205 } catch (...) {
206 if (key_is_numeric)
207 file.WriteInt(num_key, object_cast<int32>(*it));
208 else
209 file.WriteInt(str_key, object_cast<int32>(*it));
210 }
211 break;
212 case LUA_TSTRING:
213 if (key_is_numeric)
214 file.WriteString(num_key, object_cast<string>(*it));
215 else
216 file.WriteString(str_key, object_cast<string>(*it));
217 break;
218 case LUA_TTABLE:
219 if (key_is_numeric)
220 file.BeginTable(num_key);
221 else
222 file.BeginTable(str_key);
223 _CommitTable(file, object(*it));
224 file.EndTable();
225 break;
226 case LUA_TNIL:
227 case LUA_TFUNCTION:
228 case LUA_TUSERDATA:
229 case LUA_TLIGHTUSERDATA:
230 case LUA_TTHREAD:
231 default:
232 if (SCRIPT_DEBUG) {
233 _error_messages << "* ModifyScriptDescriptor::_CommitTable() detected a ";
234 if (type(*it) == LUA_TNIL) {
235 _error_messages << "nil value ";
236 } else if (type(*it) == LUA_TFUNCTION) {
237 _error_messages << "function ";
238 } else if (type(*it) == LUA_TFUNCTION) {
239 _error_messages << "function ";
240 } else if (type(*it) == LUA_TUSERDATA) {
241 _error_messages << "user data ";
242 } else if (type(*it) == LUA_TLIGHTUSERDATA) {
243 _error_messages << "light user data ";
244 } else if (type(*it) == LUA_TTHREAD) {
245 _error_messages << "thread ";
246 } else {
247 _error_messages << "unknown data type ";
248 }
249
250 if (key_is_numeric)
251 _error_messages << "key: " << num_key;
252 else
253 _error_messages << str_key;
254 _error_messages << ". It was not written to the modified file." << endl;
255 }
256 break;
257 } // switch (luabind::type(*it))
258 } // for (luabind::iterator it(table), end; it != end; ++it)
259 } // void ModifyScriptDescriptor::_CommitTable(WriteScriptDescriptor& file, luabind::object& table)
260
261 } // namespace hoa_script
262