* Small miniswig update to use less dependencies
[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   sq_pop(global_vm, 1);
96
97   // register print function
98   sq_setprintfunc(global_vm, printfunc);
99   // register default error handlers
100   sqstd_seterrorhandlers(global_vm);
101
102   // try to load default script
103   try {
104     std::string filename = "scripts/default.nut";
105     IFileStream stream(filename);
106     Scripting::compile_and_run(global_vm, stream, filename);
107   } catch(std::exception& e) {
108     log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
109   }
110 }
111
112 void exit_squirrel()
113 {
114 #ifdef ENABLE_SQDBG
115   if(debugger != NULL) {
116     sq_rdbg_shutdown(debugger);
117     debugger = NULL;
118   }
119 #endif
120
121   if (global_vm)
122     sq_close(global_vm);
123
124   global_vm = NULL;
125 }
126
127 void update_debugger()
128 {
129 #ifdef ENABLE_SQDBG
130   if(debugger != NULL)
131     sq_rdbg_update(debugger);
132 #endif
133 }
134
135 std::string squirrel2string(HSQUIRRELVM v, SQInteger i)
136 {
137   std::ostringstream os;
138   switch(sq_gettype(v, i))
139     {
140     case OT_NULL:
141       os << "<null>";
142       break;
143     case OT_BOOL: {
144       SQBool p;
145       sq_getbool(v, i, &p);
146       if (p)
147         os << "true";
148       else
149         os << "false";
150       break;
151     }
152     case OT_INTEGER: {
153       SQInteger val;
154       sq_getinteger(v, i, &val);
155       os << val;
156       break;
157     }
158     case OT_FLOAT: {
159       SQFloat val;
160       sq_getfloat(v, i, &val);
161       os << val;
162       break;
163     }
164     case OT_STRING: {
165       const SQChar* val;
166       sq_getstring(v, i, &val);
167       os << "\"" << val << "\"";
168       break;
169     }
170     case OT_TABLE: {
171       bool first = true;
172       os << "{";
173       sq_pushnull(v);  //null iterator
174       while(SQ_SUCCEEDED(sq_next(v,i-1)))
175         {
176           if (!first) {
177             os << ", ";
178           }
179           first = false;
180
181           //here -1 is the value and -2 is the key
182           os << squirrel2string(v, -2) << " => "
183              << squirrel2string(v, -1);
184
185           sq_pop(v,2); //pops key and val before the nex iteration
186         }
187       sq_pop(v, 1);
188       os << "}";
189       break;
190     }
191     case OT_ARRAY: {
192       bool first = true;
193       os << "[";
194       sq_pushnull(v);  //null iterator
195       while(SQ_SUCCEEDED(sq_next(v,i-1)))
196         {
197           if (!first) {
198             os << ", ";
199           }
200           first = false;
201
202           //here -1 is the value and -2 is the key
203           // we ignore the key, since that is just the index in an array
204           os << squirrel2string(v, -1);
205
206           sq_pop(v,2); //pops key and val before the nex iteration
207         }
208       sq_pop(v, 1);
209       os << "]";
210       break;
211     }
212     case OT_USERDATA:
213       os << "<userdata>";
214       break;
215     case OT_CLOSURE:
216       os << "<closure>";
217       break;
218     case OT_NATIVECLOSURE:
219       os << "<native closure>";
220       break;
221     case OT_GENERATOR:
222       os << "<generator>";
223       break;
224     case OT_USERPOINTER:
225       os << "userpointer";
226       break;
227     case OT_THREAD:
228       os << "<thread>";
229       break;
230     case OT_CLASS:
231       os << "<class>";
232       break;
233     case OT_INSTANCE:
234       os << "<instance>";
235       break;
236     case OT_WEAKREF:
237       os << "<weakref>";
238       break;
239     default:
240       os << "<unknown>";
241       break;
242     }
243   return os.str();
244 }
245
246 void print_squirrel_stack(HSQUIRRELVM v)
247 {
248     printf("--------------------------------------------------------------\n");
249     int count = sq_gettop(v);
250     for(int i = 1; i <= count; ++i) {
251         printf("%d: ",i);
252         switch(sq_gettype(v, i))
253         {
254             case OT_NULL:
255                 printf("null");
256                 break;
257             case OT_INTEGER: {
258                 SQInteger val;
259                 sq_getinteger(v, i, &val);
260                 printf("integer (%d)", static_cast<int> (val));
261                 break;
262             }
263             case OT_FLOAT: {
264                 SQFloat val;
265                 sq_getfloat(v, i, &val);
266                 printf("float (%f)", val);
267                 break;
268             }
269             case OT_STRING: {
270                 const SQChar* val;
271                 sq_getstring(v, i, &val);
272                 printf("string (%s)", val);
273                 break;
274             }
275             case OT_TABLE:
276                 printf("table");
277                 break;
278             case OT_ARRAY:
279                 printf("array");
280                 break;
281             case OT_USERDATA:
282                 printf("userdata");
283                 break;
284             case OT_CLOSURE:
285                 printf("closure(function)");
286                 break;
287             case OT_NATIVECLOSURE:
288                 printf("native closure(C function)");
289                 break;
290             case OT_GENERATOR:
291                 printf("generator");
292                 break;
293             case OT_USERPOINTER:
294                 printf("userpointer");
295                 break;
296             case OT_THREAD:
297                 printf("thread");
298                 break;
299             case OT_CLASS:
300                 printf("class");
301                 break;
302             case OT_INSTANCE:
303                 printf("instance");
304                 break;
305             case OT_WEAKREF:
306                 printf("weakref");
307                 break;
308             default:
309                 printf("unknown?!?");
310                 break;
311         }
312         printf("\n");
313     }
314     printf("--------------------------------------------------------------\n");
315 }
316
317 static SQInteger squirrel_read_char(SQUserPointer file)
318 {
319   std::istream* in = reinterpret_cast<std::istream*> (file);
320   char c = in->get();
321   if(in->eof())
322     return 0;
323   return c;
324 }
325
326 void compile_script(HSQUIRRELVM vm, std::istream& in, const std::string& sourcename)
327 {
328   if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in, sourcename.c_str(), true)))
329     throw SquirrelError(vm, "Couldn't parse script");
330 }
331
332 void compile_and_run(HSQUIRRELVM vm, std::istream& in,
333                      const std::string& sourcename)
334 {
335   compile_script(vm, in, sourcename);
336
337   SQInteger oldtop = sq_gettop(vm);
338
339   try {
340     sq_pushroottable(vm);
341     if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
342       throw SquirrelError(vm, "Couldn't start script");
343   } catch(...) {
344     sq_settop(vm, oldtop);
345     throw;
346   }
347
348   // we can remove the closure in case the script was not suspended
349   if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
350     sq_settop(vm, oldtop-1);
351   }
352 }
353
354 HSQOBJECT create_thread(HSQUIRRELVM vm)
355 {
356   HSQUIRRELVM new_vm = sq_newthread(vm, 64);
357   if(new_vm == NULL)
358     throw SquirrelError(vm, "Couldn't create new VM");
359
360   HSQOBJECT vm_object;
361   sq_resetobject(&vm_object);
362   if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
363     throw SquirrelError(vm, "Couldn't get squirrel thread from stack");
364   sq_addref(vm, &vm_object);
365
366   sq_pop(vm, 1);
367
368   return vm_object;
369 }
370
371 HSQOBJECT vm_to_object(HSQUIRRELVM vm)
372 {
373   HSQOBJECT object;
374   sq_resetobject(&object);
375   object._unVal.pThread = vm;
376   object._type = OT_THREAD;
377
378   return object;
379 }
380
381 HSQUIRRELVM object_to_vm(HSQOBJECT object)
382 {
383   if(object._type != OT_THREAD)
384     return NULL;
385
386   return object._unVal.pThread;
387 }
388
389 // begin: serialization functions
390
391 void store_float(HSQUIRRELVM vm, const char* name, float val)
392 {
393   sq_pushstring(vm, name, -1);
394   sq_pushfloat(vm, val);
395   if(SQ_FAILED(sq_createslot(vm, -3)))
396     throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
397 }
398
399 void store_int(HSQUIRRELVM vm, const char* name, int val)
400 {
401   sq_pushstring(vm, name, -1);
402   sq_pushinteger(vm, val);
403   if(SQ_FAILED(sq_createslot(vm, -3)))
404     throw Scripting::SquirrelError(vm, "Couldn't add int value to table");
405 }
406
407 void store_string(HSQUIRRELVM vm, const char* name, const std::string& val)
408 {
409   sq_pushstring(vm, name, -1);
410   sq_pushstring(vm, val.c_str(), val.length());
411   if(SQ_FAILED(sq_createslot(vm, -3)))
412     throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
413 }
414
415 void store_bool(HSQUIRRELVM vm, const char* name, bool val)
416 {
417   sq_pushstring(vm, name, -1);
418   sq_pushbool(vm, val ? SQTrue : SQFalse);
419   if(SQ_FAILED(sq_createslot(vm, -3)))
420     throw Scripting::SquirrelError(vm, "Couldn't add float value to table");
421 }
422
423 bool has_float(HSQUIRRELVM vm, const char* name)
424 {
425   sq_pushstring(vm, name, -1);
426   if (SQ_FAILED(sq_get(vm, -2))) return false;
427   sq_pop(vm, 1);
428   return true;
429 }
430
431 bool has_int(HSQUIRRELVM vm, const char* name)
432 {
433   return has_float(vm, name);
434 }
435
436 bool has_string(HSQUIRRELVM vm, const char* name)
437 {
438   return has_float(vm, name);
439 }
440
441 bool has_bool(HSQUIRRELVM vm, const char* name)
442 {
443   return has_float(vm, name);
444 }
445
446 float read_float(HSQUIRRELVM vm, const char* name)
447 {
448   sq_pushstring(vm, name, -1);
449   if(SQ_FAILED(sq_get(vm, -2))) {
450     std::ostringstream msg;
451     msg << "Couldn't get float value for '" << name << "' from table";
452     throw Scripting::SquirrelError(vm, msg.str());
453   }
454
455   float result;
456   if(SQ_FAILED(sq_getfloat(vm, -1, &result))) {
457     std::ostringstream msg;
458     msg << "Couldn't get float value for '" << name << "' from table";
459     throw Scripting::SquirrelError(vm, msg.str());
460   }
461   sq_pop(vm, 1);
462
463   return result;
464 }
465
466 int read_int(HSQUIRRELVM vm, const char* name)
467 {
468   sq_pushstring(vm, name, -1);
469   if(SQ_FAILED(sq_get(vm, -2))) {
470     std::ostringstream msg;
471     msg << "Couldn't get int value for '" << name << "' from table";
472     throw Scripting::SquirrelError(vm, msg.str());
473   }
474
475   SQInteger result;
476   if(SQ_FAILED(sq_getinteger(vm, -1, &result))) {
477     std::ostringstream msg;
478     msg << "Couldn't get int value for '" << name << "' from table";
479     throw Scripting::SquirrelError(vm, msg.str());
480   }
481   sq_pop(vm, 1);
482
483   return result;
484 }
485
486
487 std::string read_string(HSQUIRRELVM vm, const char* name)
488 {
489   sq_pushstring(vm, name, -1);
490   if(SQ_FAILED(sq_get(vm, -2))) {
491     std::ostringstream msg;
492     msg << "Couldn't get string value for '" << name << "' from table";
493     throw Scripting::SquirrelError(vm, msg.str());
494   }
495
496   const char* result;
497   if(SQ_FAILED(sq_getstring(vm, -1, &result))) {
498     std::ostringstream msg;
499     msg << "Couldn't get string value for '" << name << "' from table";
500     throw Scripting::SquirrelError(vm, msg.str());
501   }
502   sq_pop(vm, 1);
503
504   return std::string(result);
505 }
506
507 bool read_bool(HSQUIRRELVM vm, const char* name)
508 {
509   sq_pushstring(vm, name, -1);
510   if(SQ_FAILED(sq_get(vm, -2))) {
511     std::ostringstream msg;
512     msg << "Couldn't get bool value for '" << name << "' from table";
513     throw Scripting::SquirrelError(vm, msg.str());
514   }
515
516   SQBool result;
517   if(SQ_FAILED(sq_getbool(vm, -1, &result))) {
518     std::ostringstream msg;
519     msg << "Couldn't get bool value for '" << name << "' from table";
520     throw Scripting::SquirrelError(vm, msg.str());
521   }
522   sq_pop(vm, 1);
523
524   return result == SQTrue;
525 }
526
527 bool get_float(HSQUIRRELVM vm, const char* name, float& val) {
528   if (!has_float(vm, name)) return false;
529   val = read_float(vm, name);
530   return true;
531 }
532
533 bool get_int(HSQUIRRELVM vm, const char* name, int& val) {
534   if (!has_int(vm, name)) return false;
535   val = read_int(vm, name);
536   return true;
537 }
538
539 bool get_string(HSQUIRRELVM vm, const char* name, std::string& val) {
540   if (!has_string(vm, name)) return false;
541   val = read_string(vm, name);
542   return true;
543 }
544
545 bool get_bool(HSQUIRRELVM vm, const char* name, bool& val) {
546   if (!has_bool(vm, name)) return false;
547   val = read_bool(vm, name);
548   return true;
549 }
550
551 // end: serialization functions
552
553 }