Split of the line buffer from Console into ConsoleBuffer class
[supertux.git] / src / scripting / squirrel_util.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "scripting/squirrel_util.hpp"
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <sqstdaux.h>
23 #include <sqstdblob.h>
24 #include <sqstdmath.h>
25 #include <sqstdstring.h>
26 #include <stdarg.h>
27
28 #include "physfs/ifile_stream.hpp"
29 #include "supertux/console.hpp"
30 #include "util/log.hpp"
31
32 #ifdef ENABLE_SQDBG
33 #  include "../../external/squirrel/sqdbg/sqrdbg.h"
34 static HSQREMOTEDBG debugger = NULL;
35 #endif
36
37 namespace scripting {
38
39 HSQUIRRELVM global_vm = NULL;
40
41 static void printfunc(HSQUIRRELVM, const char* str, ...)
42 {
43   char buf[4096];
44   va_list arglist;
45   va_start(arglist, str);
46   vsnprintf(buf, sizeof(buf), str, arglist);
47   ConsoleBuffer::output << (const char*) buf << std::flush;
48   va_end(arglist);
49 }
50
51 void init_squirrel(bool enable_debugger)
52 {
53   global_vm = sq_open(64);
54   if(global_vm == NULL)
55     throw std::runtime_error("Couldn't initialize squirrel vm");
56
57   if(enable_debugger) {
58 #ifdef ENABLE_SQDBG
59     sq_enabledebuginfo(global_vm, SQTrue);
60     debugger = sq_rdbg_init(global_vm, 1234, SQFalse);
61     if(debugger == NULL)
62       throw SquirrelError(global_vm, "Couldn't initialize squirrel debugger");
63
64     sq_enabledebuginfo(global_vm, SQTrue);
65     log_info << "Waiting for debug client..." << std::endl;
66     if(SQ_FAILED(sq_rdbg_waitforconnections(debugger)))
67       throw SquirrelError(global_vm, "Waiting for debug clients failed");
68     log_info << "debug client connected." << std::endl;
69 #endif
70   }
71
72   sq_pushroottable(global_vm);
73   if(SQ_FAILED(sqstd_register_bloblib(global_vm)))
74     throw SquirrelError(global_vm, "Couldn't register blob lib");
75   if(SQ_FAILED(sqstd_register_mathlib(global_vm)))
76     throw SquirrelError(global_vm, "Couldn't register math lib");
77   if(SQ_FAILED(sqstd_register_stringlib(global_vm)))
78     throw SquirrelError(global_vm, "Couldn't register string lib");
79
80   // remove rand and srand calls from sqstdmath, we'll provide our own
81   sq_pushstring(global_vm, "srand", -1);
82   sq_deleteslot(global_vm, -2, SQFalse);
83   sq_pushstring(global_vm, "rand", -1);
84   sq_deleteslot(global_vm, -2, SQFalse);
85
86   // register supertux API
87   register_supertux_wrapper(global_vm);
88
89   sq_pop(global_vm, 1);
90
91   // register print function
92   sq_setprintfunc(global_vm, printfunc, printfunc);
93   // register default error handlers
94   sqstd_seterrorhandlers(global_vm);
95
96   // try to load default script
97   try {
98     std::string filename = "scripts/default.nut";
99     IFileStream stream(filename);
100     scripting::compile_and_run(global_vm, stream, filename);
101   } catch(std::exception& e) {
102     log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
103   }
104 }
105
106 void exit_squirrel()
107 {
108 #ifdef ENABLE_SQDBG
109   if(debugger != NULL) {
110     sq_rdbg_shutdown(debugger);
111     debugger = NULL;
112   }
113 #endif
114
115   if (global_vm)
116     sq_close(global_vm);
117
118   global_vm = NULL;
119 }
120
121 void update_debugger()
122 {
123 #ifdef ENABLE_SQDBG
124   if(debugger != NULL)
125     sq_rdbg_update(debugger);
126 #endif
127 }
128
129 std::string squirrel2string(HSQUIRRELVM v, SQInteger i)
130 {
131   std::ostringstream os;
132   switch(sq_gettype(v, i))
133   {
134     case OT_NULL:
135       os << "<null>";
136       break;
137     case OT_BOOL: {
138       SQBool p;
139       sq_getbool(v, i, &p);
140       if (p)
141         os << "true";
142       else
143         os << "false";
144       break;
145     }
146     case OT_INTEGER: {
147       SQInteger val;
148       sq_getinteger(v, i, &val);
149       os << val;
150       break;
151     }
152     case OT_FLOAT: {
153       SQFloat val;
154       sq_getfloat(v, i, &val);
155       os << val;
156       break;
157     }
158     case OT_STRING: {
159       const SQChar* val;
160       sq_getstring(v, i, &val);
161       os << "\"" << val << "\"";
162       break;
163     }
164     case OT_TABLE: {
165       bool first = true;
166       os << "{";
167       sq_pushnull(v);  //null iterator
168       while(SQ_SUCCEEDED(sq_next(v,i-1)))
169       {
170         if (!first) {
171           os << ", ";
172         }
173         first = false;
174
175         //here -1 is the value and -2 is the key
176         os << squirrel2string(v, -2) << " => "
177            << squirrel2string(v, -1);
178
179         sq_pop(v,2); //pops key and val before the nex iteration
180       }
181       sq_pop(v, 1);
182       os << "}";
183       break;
184     }
185     case OT_ARRAY: {
186       bool first = true;
187       os << "[";
188       sq_pushnull(v);  //null iterator
189       while(SQ_SUCCEEDED(sq_next(v,i-1)))
190       {
191         if (!first) {
192           os << ", ";
193         }
194         first = false;
195
196         //here -1 is the value and -2 is the key
197         // we ignore the key, since that is just the index in an array
198         os << squirrel2string(v, -1);
199
200         sq_pop(v,2); //pops key and val before the nex iteration
201       }
202       sq_pop(v, 1);
203       os << "]";
204       break;
205     }
206     case OT_USERDATA:
207       os << "<userdata>";
208       break;
209     case OT_CLOSURE:
210       os << "<closure>";
211       break;
212     case OT_NATIVECLOSURE:
213       os << "<native closure>";
214       break;
215     case OT_GENERATOR:
216       os << "<generator>";
217       break;
218     case OT_USERPOINTER:
219       os << "userpointer";
220       break;
221     case OT_THREAD:
222       os << "<thread>";
223       break;
224     case OT_CLASS:
225       os << "<class>";
226       break;
227     case OT_INSTANCE:
228       os << "<instance>";
229       break;
230     case OT_WEAKREF:
231       os << "<weakref>";
232       break;
233     default:
234       os << "<unknown>";
235       break;
236   }
237   return os.str();
238 }
239
240 void print_squirrel_stack(HSQUIRRELVM v)
241 {
242   printf("--------------------------------------------------------------\n");
243   int count = sq_gettop(v);
244   for(int i = 1; i <= count; ++i) {
245     printf("%d: ",i);
246     switch(sq_gettype(v, i))
247     {
248       case OT_NULL:
249         printf("null");
250         break;
251       case OT_INTEGER: {
252         SQInteger val;
253         sq_getinteger(v, i, &val);
254         printf("integer (%d)", static_cast<int> (val));
255         break;
256       }
257       case OT_FLOAT: {
258         SQFloat val;
259         sq_getfloat(v, i, &val);
260         printf("float (%f)", val);
261         break;
262       }
263       case OT_STRING: {
264         const SQChar* val;
265         sq_getstring(v, i, &val);
266         printf("string (%s)", val);
267         break;
268       }
269       case OT_TABLE:
270         printf("table");
271         break;
272       case OT_ARRAY:
273         printf("array");
274         break;
275       case OT_USERDATA:
276         printf("userdata");
277         break;
278       case OT_CLOSURE:
279         printf("closure(function)");
280         break;
281       case OT_NATIVECLOSURE:
282         printf("native closure(C function)");
283         break;
284       case OT_GENERATOR:
285         printf("generator");
286         break;
287       case OT_USERPOINTER:
288         printf("userpointer");
289         break;
290       case OT_THREAD:
291         printf("thread");
292         break;
293       case OT_CLASS:
294         printf("class");
295         break;
296       case OT_INSTANCE:
297         printf("instance");
298         break;
299       case OT_WEAKREF:
300         printf("weakref");
301         break;
302       default:
303         printf("unknown?!?");
304         break;
305     }
306     printf("\n");
307   }
308   printf("--------------------------------------------------------------\n");
309 }
310
311 SQInteger squirrel_read_char(SQUserPointer file)
312 {
313   std::istream* in = reinterpret_cast<std::istream*> (file);
314   char c = in->get();
315   if(in->eof())
316     return 0;
317   return c;
318 }
319
320 void compile_script(HSQUIRRELVM vm, std::istream& in, const std::string& sourcename)
321 {
322   if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in, sourcename.c_str(), true)))
323     throw SquirrelError(vm, "Couldn't parse script");
324 }
325
326 void compile_and_run(HSQUIRRELVM vm, std::istream& in,
327                      const std::string& sourcename)
328 {
329   compile_script(vm, in, sourcename);
330
331   SQInteger oldtop = sq_gettop(vm);
332
333   try {
334     sq_pushroottable(vm);
335     if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
336       throw SquirrelError(vm, "Couldn't start script");
337   } catch(...) {
338     sq_settop(vm, oldtop);
339     throw;
340   }
341
342   // we can remove the closure in case the script was not suspended
343   if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
344     sq_settop(vm, oldtop-1);
345   }
346 }
347
348 HSQOBJECT create_thread(HSQUIRRELVM vm)
349 {
350   HSQUIRRELVM new_vm = sq_newthread(vm, 64);
351   if(new_vm == NULL)
352     throw SquirrelError(vm, "Couldn't create new VM");
353
354   HSQOBJECT vm_object;
355   sq_resetobject(&vm_object);
356   if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
357     throw SquirrelError(vm, "Couldn't get squirrel thread from stack");
358   sq_addref(vm, &vm_object);
359
360   sq_pop(vm, 1);
361
362   return vm_object;
363 }
364
365 HSQOBJECT vm_to_object(HSQUIRRELVM vm)
366 {
367   HSQOBJECT object;
368   sq_resetobject(&object);
369   object._unVal.pThread = vm;
370   object._type = OT_THREAD;
371
372   return object;
373 }
374
375 HSQUIRRELVM object_to_vm(HSQOBJECT object)
376 {
377   if(object._type != OT_THREAD)
378     return NULL;
379
380   return object._unVal.pThread;
381 }
382
383 // begin: serialization functions
384
385 void store_float(HSQUIRRELVM vm, const char* name, float val)
386 {
387   sq_pushstring(vm, name, -1);
388   sq_pushfloat(vm, val);
389   if(SQ_FAILED(sq_createslot(vm, -3)))
390     throw scripting::SquirrelError(vm, "Couldn't add float value to table");
391 }
392
393 void store_int(HSQUIRRELVM vm, const char* name, int val)
394 {
395   sq_pushstring(vm, name, -1);
396   sq_pushinteger(vm, val);
397   if(SQ_FAILED(sq_createslot(vm, -3)))
398     throw scripting::SquirrelError(vm, "Couldn't add int value to table");
399 }
400
401 void store_string(HSQUIRRELVM vm, const char* name, const std::string& val)
402 {
403   sq_pushstring(vm, name, -1);
404   sq_pushstring(vm, val.c_str(), val.length());
405   if(SQ_FAILED(sq_createslot(vm, -3)))
406     throw scripting::SquirrelError(vm, "Couldn't add float value to table");
407 }
408
409 void store_bool(HSQUIRRELVM vm, const char* name, bool val)
410 {
411   sq_pushstring(vm, name, -1);
412   sq_pushbool(vm, val ? SQTrue : SQFalse);
413   if(SQ_FAILED(sq_createslot(vm, -3)))
414     throw scripting::SquirrelError(vm, "Couldn't add float value to table");
415 }
416
417 bool has_float(HSQUIRRELVM vm, const char* name)
418 {
419   sq_pushstring(vm, name, -1);
420   if (SQ_FAILED(sq_get(vm, -2))) return false;
421   sq_pop(vm, 1);
422   return true;
423 }
424
425 bool has_int(HSQUIRRELVM vm, const char* name)
426 {
427   return has_float(vm, name);
428 }
429
430 bool has_string(HSQUIRRELVM vm, const char* name)
431 {
432   return has_float(vm, name);
433 }
434
435 bool has_bool(HSQUIRRELVM vm, const char* name)
436 {
437   return has_float(vm, name);
438 }
439
440 float read_float(HSQUIRRELVM vm, const char* name)
441 {
442   sq_pushstring(vm, name, -1);
443   if(SQ_FAILED(sq_get(vm, -2))) {
444     std::ostringstream msg;
445     msg << "Couldn't get float value for '" << name << "' from table";
446     throw scripting::SquirrelError(vm, msg.str());
447   }
448
449   float result;
450   if(SQ_FAILED(sq_getfloat(vm, -1, &result))) {
451     std::ostringstream msg;
452     msg << "Couldn't get float value for '" << name << "' from table";
453     throw scripting::SquirrelError(vm, msg.str());
454   }
455   sq_pop(vm, 1);
456
457   return result;
458 }
459
460 int read_int(HSQUIRRELVM vm, const char* name)
461 {
462   sq_pushstring(vm, name, -1);
463   if(SQ_FAILED(sq_get(vm, -2))) {
464     std::ostringstream msg;
465     msg << "Couldn't get int value for '" << name << "' from table";
466     throw scripting::SquirrelError(vm, msg.str());
467   }
468
469   SQInteger result;
470   if(SQ_FAILED(sq_getinteger(vm, -1, &result))) {
471     std::ostringstream msg;
472     msg << "Couldn't get int value for '" << name << "' from table";
473     throw scripting::SquirrelError(vm, msg.str());
474   }
475   sq_pop(vm, 1);
476
477   return result;
478 }
479
480 std::string read_string(HSQUIRRELVM vm, const char* name)
481 {
482   sq_pushstring(vm, name, -1);
483   if(SQ_FAILED(sq_get(vm, -2))) {
484     std::ostringstream msg;
485     msg << "Couldn't get string value for '" << name << "' from table";
486     throw scripting::SquirrelError(vm, msg.str());
487   }
488
489   const char* result;
490   if(SQ_FAILED(sq_getstring(vm, -1, &result))) {
491     std::ostringstream msg;
492     msg << "Couldn't get string value for '" << name << "' from table";
493     throw scripting::SquirrelError(vm, msg.str());
494   }
495   sq_pop(vm, 1);
496
497   return std::string(result);
498 }
499
500 bool read_bool(HSQUIRRELVM vm, const char* name)
501 {
502   sq_pushstring(vm, name, -1);
503   if(SQ_FAILED(sq_get(vm, -2))) {
504     std::ostringstream msg;
505     msg << "Couldn't get bool value for '" << name << "' from table";
506     throw scripting::SquirrelError(vm, msg.str());
507   }
508
509   SQBool result;
510   if(SQ_FAILED(sq_getbool(vm, -1, &result))) {
511     std::ostringstream msg;
512     msg << "Couldn't get bool value for '" << name << "' from table";
513     throw scripting::SquirrelError(vm, msg.str());
514   }
515   sq_pop(vm, 1);
516
517   return result == SQTrue;
518 }
519
520 bool get_float(HSQUIRRELVM vm, const char* name, float& val) {
521   if (!has_float(vm, name)) return false;
522   val = read_float(vm, name);
523   return true;
524 }
525
526 bool get_int(HSQUIRRELVM vm, const char* name, int& val) {
527   if (!has_int(vm, name)) return false;
528   val = read_int(vm, name);
529   return true;
530 }
531
532 bool get_string(HSQUIRRELVM vm, const char* name, std::string& val) {
533   if (!has_string(vm, name)) return false;
534   val = read_string(vm, name);
535   return true;
536 }
537
538 bool get_bool(HSQUIRRELVM vm, const char* name, bool& val) {
539   if (!has_bool(vm, name)) return false;
540   val = read_bool(vm, name);
541   return true;
542 }
543
544 // end: serialization functions
545
546 }
547
548 /* EOF */