1 | // SuperTux |
2 | // Copyright (C) 2006 Matthias Braun <matze@braunis.de> |
3 | // 2018 Ingo Ruhnke <grumbel@gmail.com> |
4 | // |
5 | // This program is free software: you can redistribute it and/or modify |
6 | // it under the terms of the GNU General Public License as published by |
7 | // the Free Software Foundation, either version 3 of the License, or |
8 | // (at your option) any later version. |
9 | // |
10 | // This program is distributed in the hope that it will be useful, |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | // GNU General Public License for more details. |
14 | // |
15 | // You should have received a copy of the GNU General Public License |
16 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | |
18 | #include "squirrel/squirrel_vm.hpp" |
19 | |
20 | #include <stdexcept> |
21 | |
22 | #include "squirrel/squirrel_error.hpp" |
23 | #include "squirrel/squirrel_util.hpp" |
24 | |
25 | SquirrelVM::SquirrelVM() : |
26 | m_vm() |
27 | { |
28 | m_vm = sq_open(64); |
29 | if (m_vm == nullptr) |
30 | throw std::runtime_error("Couldn't initialize squirrel vm" ); |
31 | } |
32 | |
33 | SquirrelVM::~SquirrelVM() |
34 | { |
35 | #ifdef ENABLE_SQDBG |
36 | if (debugger != nullptr) { |
37 | sq_rdbg_shutdown(debugger); |
38 | debugger = nullptr; |
39 | } |
40 | #endif |
41 | |
42 | sq_close(m_vm); |
43 | } |
44 | |
45 | void |
46 | SquirrelVM::begin_table(const char* name) |
47 | { |
48 | sq_pushstring(m_vm, name, -1); |
49 | sq_newtable(m_vm); |
50 | } |
51 | |
52 | void |
53 | SquirrelVM::end_table(const char* name) |
54 | { |
55 | if (SQ_FAILED(sq_createslot(m_vm, -3))) |
56 | throw SquirrelError(m_vm, "Failed to create '" + std::string(name) + "' table entry" ); |
57 | } |
58 | |
59 | void |
60 | SquirrelVM::create_empty_table(const char* name) |
61 | { |
62 | begin_table(name); |
63 | end_table(name); |
64 | } |
65 | |
66 | bool |
67 | SquirrelVM::has_property(const char* name) |
68 | { |
69 | sq_pushstring(m_vm, name, -1); |
70 | if (SQ_FAILED(sq_get(m_vm, -2))) return false; |
71 | sq_pop(m_vm, 1); |
72 | return true; |
73 | } |
74 | |
75 | void |
76 | SquirrelVM::store_bool(const char* name, bool val) |
77 | { |
78 | sq_pushstring(m_vm, name, -1); |
79 | sq_pushbool(m_vm, val ? SQTrue : SQFalse); |
80 | if (SQ_FAILED(sq_createslot(m_vm, -3))) |
81 | throw SquirrelError(m_vm, "Couldn't add float value to table" ); |
82 | } |
83 | |
84 | void |
85 | SquirrelVM::store_int(const char* name, int val) |
86 | { |
87 | sq_pushstring(m_vm, name, -1); |
88 | sq_pushinteger(m_vm, val); |
89 | if (SQ_FAILED(sq_createslot(m_vm, -3))) |
90 | throw SquirrelError(m_vm, "Couldn't add int value to table" ); |
91 | } |
92 | |
93 | void |
94 | SquirrelVM::store_float(const char* name, float val) |
95 | { |
96 | sq_pushstring(m_vm, name, -1); |
97 | sq_pushfloat(m_vm, val); |
98 | if (SQ_FAILED(sq_createslot(m_vm, -3))) |
99 | throw SquirrelError(m_vm, "Couldn't add float value to table" ); |
100 | } |
101 | |
102 | void |
103 | SquirrelVM::store_string(const char* name, const std::string& val) |
104 | { |
105 | sq_pushstring(m_vm, name, -1); |
106 | sq_pushstring(m_vm, val.c_str(), val.length()); |
107 | if (SQ_FAILED(sq_createslot(m_vm, -3))) |
108 | throw SquirrelError(m_vm, "Couldn't add float value to table" ); |
109 | } |
110 | |
111 | void |
112 | SquirrelVM::store_object(const char* name, const HSQOBJECT& val) |
113 | { |
114 | sq_pushstring(m_vm, name, -1); |
115 | sq_pushobject(m_vm, val); |
116 | if (SQ_FAILED(sq_createslot(m_vm, -3))) |
117 | throw SquirrelError(m_vm, "Couldn't add object value to table" ); |
118 | } |
119 | |
120 | bool |
121 | SquirrelVM::get_bool(const char* name, bool& val) |
122 | { |
123 | if (!has_property(name)) return false; |
124 | val = read_bool(name); |
125 | return true; |
126 | } |
127 | |
128 | bool |
129 | SquirrelVM::get_int(const char* name, int& val) |
130 | { |
131 | if (!has_property(name)) return false; |
132 | val = read_int(name); |
133 | return true; |
134 | } |
135 | |
136 | bool |
137 | SquirrelVM::get_float(const char* name, float& val) |
138 | { |
139 | if (!has_property(name)) return false; |
140 | val = read_float(name); |
141 | return true; |
142 | } |
143 | |
144 | bool |
145 | SquirrelVM::get_string(const char* name, std::string& val) |
146 | { |
147 | if (!has_property(name)) return false; |
148 | val = read_string(name); |
149 | return true; |
150 | } |
151 | |
152 | bool |
153 | SquirrelVM::read_bool(const char* name) |
154 | { |
155 | get_table_entry(name); |
156 | |
157 | SQBool result; |
158 | if (SQ_FAILED(sq_getbool(m_vm, -1, &result))) { |
159 | std::ostringstream msg; |
160 | msg << "Couldn't get bool value for '" << name << "' from table" ; |
161 | throw SquirrelError(m_vm, msg.str()); |
162 | } |
163 | sq_pop(m_vm, 1); |
164 | |
165 | return result == SQTrue; |
166 | } |
167 | |
168 | int |
169 | SquirrelVM::read_int(const char* name) |
170 | { |
171 | get_table_entry(name); |
172 | |
173 | SQInteger result; |
174 | if (SQ_FAILED(sq_getinteger(m_vm, -1, &result))) { |
175 | std::ostringstream msg; |
176 | msg << "Couldn't get int value for '" << name << "' from table" ; |
177 | throw SquirrelError(m_vm, msg.str()); |
178 | } |
179 | sq_pop(m_vm, 1); |
180 | |
181 | return static_cast<int>(result); |
182 | } |
183 | |
184 | float |
185 | SquirrelVM::read_float(const char* name) |
186 | { |
187 | get_table_entry(name); |
188 | |
189 | float result; |
190 | if (SQ_FAILED(sq_getfloat(m_vm, -1, &result))) { |
191 | std::ostringstream msg; |
192 | msg << "Couldn't get float value for '" << name << "' from table" ; |
193 | throw SquirrelError(m_vm, msg.str()); |
194 | } |
195 | sq_pop(m_vm, 1); |
196 | |
197 | return result; |
198 | } |
199 | |
200 | std::string |
201 | SquirrelVM::read_string(const char* name) |
202 | { |
203 | get_table_entry(name); |
204 | |
205 | const char* result; |
206 | if (SQ_FAILED(sq_getstring(m_vm, -1, &result))) { |
207 | std::ostringstream msg; |
208 | msg << "Couldn't get string value for '" << name << "' from table" ; |
209 | throw SquirrelError(m_vm, msg.str()); |
210 | } |
211 | sq_pop(m_vm, 1); |
212 | |
213 | return std::string(result); |
214 | } |
215 | |
216 | void |
217 | SquirrelVM::get_table_entry(const std::string& name) |
218 | { |
219 | sq_pushstring(m_vm, name.c_str(), -1); |
220 | if (SQ_FAILED(sq_get(m_vm, -2))) |
221 | { |
222 | std::ostringstream msg; |
223 | msg << "failed to get '" << name << "' table entry" ; |
224 | throw SquirrelError(m_vm, msg.str()); |
225 | } |
226 | else |
227 | { |
228 | // successfully placed result on stack |
229 | } |
230 | } |
231 | |
232 | void |
233 | SquirrelVM::get_or_create_table_entry(const std::string& name) |
234 | { |
235 | try |
236 | { |
237 | get_table_entry(name); |
238 | } |
239 | catch(std::exception&) |
240 | { |
241 | create_empty_table(name.c_str()); |
242 | get_table_entry(name); |
243 | } |
244 | } |
245 | |
246 | void |
247 | SquirrelVM::delete_table_entry(const char* name) |
248 | { |
249 | sq_pushstring(m_vm, name, -1); |
250 | if (SQ_FAILED(sq_deleteslot(m_vm, -2, false))) |
251 | { |
252 | // Something failed while deleting the table entry. |
253 | // Key doesn't exist? |
254 | } |
255 | } |
256 | |
257 | void |
258 | SquirrelVM::rename_table_entry(const char* oldname, const char* newname) |
259 | { |
260 | SQInteger oldtop = sq_gettop(m_vm); |
261 | |
262 | // push key |
263 | sq_pushstring(m_vm, newname, -1); |
264 | |
265 | // push value and delete old oldname |
266 | sq_pushstring(m_vm, oldname, -1); |
267 | if (SQ_FAILED(sq_deleteslot(m_vm, oldtop, SQTrue))) { |
268 | sq_settop(m_vm, oldtop); |
269 | throw SquirrelError(m_vm, "Couldn't find 'oldname' entry in table" ); |
270 | } |
271 | |
272 | // create new entry |
273 | sq_createslot(m_vm, -3); |
274 | |
275 | sq_settop(m_vm, oldtop); |
276 | } |
277 | |
278 | std::vector<std::string> |
279 | SquirrelVM::get_table_keys() |
280 | { |
281 | std::vector<std::string> keys; |
282 | |
283 | sq_pushnull(m_vm); |
284 | while (SQ_SUCCEEDED(sq_next(m_vm, -2))) |
285 | { |
286 | //here -1 is the value and -2 is the key |
287 | const char* result; |
288 | if (SQ_FAILED(sq_getstring(m_vm, -2, &result))) |
289 | { |
290 | throw SquirrelError(m_vm, "Couldn't get string value for key" ); |
291 | } |
292 | else |
293 | { |
294 | keys.push_back(result); |
295 | } |
296 | |
297 | // pops key and val before the next iteration |
298 | sq_pop(m_vm, 2); |
299 | } |
300 | |
301 | return keys; |
302 | } |
303 | |
304 | HSQOBJECT |
305 | SquirrelVM::create_thread() |
306 | { |
307 | HSQUIRRELVM new_vm = sq_newthread(m_vm, 64); |
308 | if (new_vm == nullptr) |
309 | throw SquirrelError(m_vm, "Couldn't create new VM" ); |
310 | |
311 | HSQOBJECT vm_object; |
312 | sq_resetobject(&vm_object); |
313 | if (SQ_FAILED(sq_getstackobj(m_vm, -1, &vm_object))) |
314 | throw SquirrelError(m_vm, "Couldn't get squirrel thread from stack" ); |
315 | sq_addref(m_vm, &vm_object); |
316 | |
317 | sq_pop(m_vm, 1); |
318 | |
319 | return vm_object; |
320 | } |
321 | |
322 | /* EOF */ |
323 | |