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
25SquirrelVM::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
33SquirrelVM::~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
45void
46SquirrelVM::begin_table(const char* name)
47{
48 sq_pushstring(m_vm, name, -1);
49 sq_newtable(m_vm);
50}
51
52void
53SquirrelVM::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
59void
60SquirrelVM::create_empty_table(const char* name)
61{
62 begin_table(name);
63 end_table(name);
64}
65
66bool
67SquirrelVM::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
75void
76SquirrelVM::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
84void
85SquirrelVM::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
93void
94SquirrelVM::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
102void
103SquirrelVM::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
111void
112SquirrelVM::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
120bool
121SquirrelVM::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
128bool
129SquirrelVM::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
136bool
137SquirrelVM::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
144bool
145SquirrelVM::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
152bool
153SquirrelVM::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
168int
169SquirrelVM::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
184float
185SquirrelVM::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
200std::string
201SquirrelVM::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
216void
217SquirrelVM::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
232void
233SquirrelVM::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
246void
247SquirrelVM::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
257void
258SquirrelVM::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
278std::vector<std::string>
279SquirrelVM::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
304HSQOBJECT
305SquirrelVM::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