f16bb63bbec2284a98f9273aea8f8e2d6fd6497a
[supertux.git] / src / scripting / squirrel_util.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 #include <config.h>
20
21 #include <stdexcept>
22 #include <sstream>
23 #include <stdarg.h>
24 #include <squirrel.h>
25 #include <sqstdmath.h>
26 #include <sqstdblob.h>
27 #include <sqstdstring.h>
28 #include <sqstdaux.h>
29 #include <sqstdio.h>
30 #include "squirrel_util.hpp"
31 #include "log.hpp"
32 #include "level.hpp"
33 #include "physfs/physfs_stream.hpp"
34 #include "../random_generator.hpp"
35
36 #ifdef ENABLE_SQDBG
37 #include <sqdbg/sqrdbg.h>
38
39 static HSQREMOTEDBG debugger = NULL;
40 #endif
41
42 namespace Scripting
43 {
44
45 HSQUIRRELVM global_vm = NULL;
46
47 static void printfunc(HSQUIRRELVM, const char* str, ...)
48 {
49   char buf[4096];
50   va_list arglist;
51   va_start(arglist, str);
52   vsprintf(buf, str, arglist);
53   Console::output << (const char*) buf << std::flush;
54   va_end(arglist);
55 }
56
57 void init_squirrel(bool enable_debugger)
58 {
59   global_vm = sq_open(64);
60   if(global_vm == NULL)
61     throw std::runtime_error("Couldn't initialize squirrel vm");
62
63   if(enable_debugger) {
64 #ifdef ENABLE_SQDBG
65     sq_enabledebuginfo(global_vm, SQTrue);
66     debugger = sq_rdbg_init(global_vm, 1234, SQFalse);
67     if(debugger == NULL)
68       throw SquirrelError(global_vm, "Couldn't initialize squirrel debugger");
69
70     sq_enabledebuginfo(global_vm, SQTrue);
71     log_info << "Waiting for debug client..." << std::endl;
72     if(SQ_FAILED(sq_rdbg_waitforconnections(debugger)))
73       throw SquirrelError(global_vm, "Waiting for debug clients failed");
74     log_info << "debug client connected." << std::endl;
75 #endif
76   }
77
78   sq_pushroottable(global_vm);
79   if(SQ_FAILED(sqstd_register_bloblib(global_vm)))
80     throw SquirrelError(global_vm, "Couldn't register blob lib");
81   if(SQ_FAILED(sqstd_register_mathlib(global_vm)))
82     throw SquirrelError(global_vm, "Couldn't register math lib");
83   if(SQ_FAILED(sqstd_register_stringlib(global_vm)))
84     throw SquirrelError(global_vm, "Couldn't register string lib");
85
86   // remove rand and srand calls from sqstdmath, we'll provide our own
87   sq_pushstring(global_vm, "srand", -1);
88   sq_deleteslot(global_vm, -2, SQFalse);
89   sq_pushstring(global_vm, "rand", -1);
90   sq_deleteslot(global_vm, -2, SQFalse);
91
92   // register supertux API
93   register_supertux_wrapper(global_vm);
94
95   // TODO remove this at some point... it shoud just be functions not an object
96   expose_object(global_vm, -1, new Scripting::Level(), "Level", true);
97
98   sq_pop(global_vm, 1);
99
100   // register print function
101   sq_setprintfunc(global_vm, printfunc);
102   // register default error handlers
103   sqstd_seterrorhandlers(global_vm);
104
105   // try to load default script
106   try {
107     std::string filename = "scripts/default.nut";
108     IFileStream stream(filename);
109     Scripting::compile_and_run(global_vm, stream, filename);
110   } catch(std::exception& e) {
111     log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
112   }
113 }
114
115 void exit_squirrel()
116 {
117 #ifdef ENABLE_SQDBG
118   if(debugger != NULL) {
119     sq_rdbg_shutdown(debugger);
120     debugger = NULL;
121   }
122 #endif
123
124   if (global_vm)
125     sq_close(global_vm);
126
127   global_vm = NULL;
128 }
129
130 void update_debugger()
131 {
132 #ifdef ENABLE_SQDBG
133   if(debugger != NULL)
134     sq_rdbg_update(debugger);
135 #endif
136 }
137
138 std::string squirrel2string(HSQUIRRELVM v, SQInteger i)
139 {
140   std::ostringstream os;
141   switch(sq_gettype(v, i))
142     {
143     case OT_NULL:
144       os << "<null>";
145       break;
146     case OT_BOOL: {
147       SQBool p;
148       sq_getbool(v, i, &p);
149       if (p)
150         os << "true";
151       else
152         os << "false";
153       break;
154     }
155     case OT_INTEGER: {
156       SQInteger val;
157       sq_getinteger(v, i, &val);
158       os << val;
159       break;
160     }
161     case OT_FLOAT: {
162       SQFloat val;
163       sq_getfloat(v, i, &val);
164       os << val;
165       break;
166     }
167     case OT_STRING: {
168       const SQChar* val;
169       sq_getstring(v, i, &val);
170       os << "\"" << val << "\"";
171       break;
172     }
173     case OT_TABLE: {
174       bool first = true;
175       os << "{";
176       sq_pushnull(v);  //null iterator
177       while(SQ_SUCCEEDED(sq_next(v,i-1)))
178         {
179           if (!first) {
180             os << ", ";
181           }
182           first = false;
183
184           //here -1 is the value and -2 is the key
185           os << squirrel2string(v, -2) << " => "
186              << squirrel2string(v, -1);
187
188           sq_pop(v,2); //pops key and val before the nex iteration
189         }
190       sq_pop(v, 1);
191       os << "}";
192       break;
193     }
194     case OT_ARRAY: {
195       bool first = true;
196       os << "[";
197       sq_pushnull(v);  //null iterator
198       while(SQ_SUCCEEDED(sq_next(v,i-1)))
199         {
200           if (!first) {
201             os << ", ";
202           }
203           first = false;
204
205           //here -1 is the value and -2 is the key
206           // we ignore the key, since that is just the index in an array
207           os << squirrel2string(v, -1);
208
209           sq_pop(v,2); //pops key and val before the nex iteration
210         }
211       sq_pop(v, 1);
212       os << "]";
213       break;
214     }
215     case OT_USERDATA:
216       os << "<userdata>";
217       break;
218     case OT_CLOSURE:
219       os << "<closure>";
220       break;
221     case OT_NATIVECLOSURE:
222       os << "<native closure>";
223       break;
224     case OT_GENERATOR:
225       os << "<generator>";
226       break;
227     case OT_USERPOINTER:
228       os << "userpointer";
229       break;
230     case OT_THREAD:
231       os << "<thread>";
232       break;
233     case OT_CLASS:
234       os << "<class>";
235       break;
236     case OT_INSTANCE:
237       os << "<instance>";
238       break;
239     case OT_WEAKREF:
240       os << "<weakref>";
241       break;
242     default:
243       os << "<unknown>";
244       break;
245     }
246   return os.str();
247 }
248
249 void print_squirrel_stack(HSQUIRRELVM v)
250 {
251     printf("--------------------------------------------------------------\n");
252     int count = sq_gettop(v);
253     for(int i = 1; i <= count; ++i) {
254         printf("%d: ",i);
255         switch(sq_gettype(v, i))
256         {
257             case OT_NULL:
258                 printf("null");
259                 break;
260             case OT_INTEGER: {
261                 SQInteger val;
262                 sq_getinteger(v, i, &val);
263                 printf("integer (%d)", static_cast<int> (val));
264                 break;
265             }
266             case OT_FLOAT: {
267                 SQFloat val;
268                 sq_getfloat(v, i, &val);
269                 printf("float (%f)", val);
270                 break;
271             }
272             case OT_STRING: {
273                 const SQChar* val;
274                 sq_getstring(v, i, &val);
275                 printf("string (%s)", val);
276                 break;
277             }
278             case OT_TABLE:
279                 printf("table");
280                 break;
281             case OT_ARRAY:
282                 printf("array");
283                 break;
284             case OT_USERDATA:
285                 printf("userdata");
286                 break;
287             case OT_CLOSURE:
288                 printf("closure(function)");
289                 break;
290             case OT_NATIVECLOSURE:
291                 printf("native closure(C function)");
292                 break;
293             case OT_GENERATOR:
294                 printf("generator");
295                 break;
296             case OT_USERPOINTER:
297                 printf("userpointer");
298                 break;
299             case OT_THREAD:
300                 printf("thread");
301                 break;
302             case OT_CLASS:
303                 printf("class");
304                 break;
305             case OT_INSTANCE:
306                 printf("instance");
307                 break;
308             case OT_WEAKREF:
309                 printf("weakref");
310                 break;
311             default:
312                 printf("unknown?!?");
313                 break;
314         }
315         printf("\n");
316     }
317     printf("--------------------------------------------------------------\n");
318 }
319
320 static SQInteger squirrel_read_char(SQUserPointer file)
321 {
322   std::istream* in = reinterpret_cast<std::istream*> (file);
323   char c = in->get();
324   if(in->eof())
325     return 0;
326   return c;
327 }
328
329 void compile_script(HSQUIRRELVM vm, std::istream& in, const std::string& sourcename)
330 {
331   if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in, sourcename.c_str(), true)))
332     throw SquirrelError(vm, "Couldn't parse script");
333 }
334
335 void compile_and_run(HSQUIRRELVM vm, std::istream& in,
336                      const std::string& sourcename)
337 {
338   compile_script(vm, in, sourcename);
339
340   SQInteger oldtop = sq_gettop(vm);
341
342   try {
343     sq_pushroottable(vm);
344     if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
345       throw SquirrelError(vm, "Couldn't start script");
346   } catch(...) {
347     sq_settop(vm, oldtop);
348     throw;
349   }
350
351   // we can remove the closure in case the script was not suspended
352   if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
353     sq_settop(vm, oldtop-1);
354   }
355 }
356
357 HSQOBJECT create_thread(HSQUIRRELVM vm)
358 {
359   HSQUIRRELVM new_vm = sq_newthread(vm, 64);
360   if(new_vm == NULL)
361     throw SquirrelError(vm, "Couldn't create new VM");
362
363   HSQOBJECT vm_object;
364   sq_resetobject(&vm_object);
365   if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
366     throw SquirrelError(vm, "Couldn't get squirrel thread from stack");
367   sq_addref(vm, &vm_object);
368
369   sq_pop(vm, 1);
370
371   return vm_object;
372 }
373
374 HSQOBJECT vm_to_object(HSQUIRRELVM vm)
375 {
376   HSQOBJECT object;
377   sq_resetobject(&object);
378   object._unVal.pThread = vm;
379   object._type = OT_THREAD;
380
381   return object;
382 }
383
384 HSQUIRRELVM object_to_vm(HSQOBJECT object)
385 {
386   if(object._type != OT_THREAD)
387     return NULL;
388
389   return object._unVal.pThread;
390 }
391
392 }