Supertux can now run even if we were unable to open an audio device
[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
35 #ifdef ENABLE_SQDBG
36 #include <sqdbg/sqrdbg.h>
37
38 static HSQREMOTEDBG debugger = NULL;
39 #endif
40
41 namespace Scripting
42 {
43
44 HSQUIRRELVM global_vm = NULL;
45
46 static void printfunc(HSQUIRRELVM, const char* str, ...)
47 {
48   char buf[4096];
49   va_list arglist;
50   va_start(arglist, str);
51   vsprintf(buf, str, arglist);
52   Console::output << (const char*) buf << std::flush;
53   va_end(arglist);
54 }
55
56 void init_squirrel(bool enable_debugger)
57 {
58   global_vm = sq_open(64);
59   if(global_vm == NULL)
60     throw std::runtime_error("Couldn't initialize squirrel vm");
61
62   if(enable_debugger) {
63 #ifdef ENABLE_SQDBG
64     sq_enabledebuginfo(global_vm, SQTrue);
65     debugger = sq_rdbg_init(global_vm, 1234, SQFalse);
66     if(debugger == NULL)
67       throw SquirrelError(global_vm, "Couldn't initialize squirrel debugger");
68   
69     sq_enabledebuginfo(global_vm, SQTrue);
70     log_info << "Waiting for debug client..." << std::endl;
71     if(SQ_FAILED(sq_rdbg_waitforconnections(debugger)))
72       throw SquirrelError(global_vm, "Waiting for debug clients failed");
73     log_info << "debug client connected." << std::endl;
74 #endif
75   }
76
77   sq_pushroottable(global_vm);
78   if(sqstd_register_bloblib(global_vm) < 0)
79     throw SquirrelError(global_vm, "Couldn't register blob lib");
80   if(sqstd_register_mathlib(global_vm) < 0)
81     throw SquirrelError(global_vm, "Couldn't register math lib");
82   if(sqstd_register_stringlib(global_vm) < 0)
83     throw SquirrelError(global_vm, "Couldn't register string lib");
84   // register supertux API
85   register_supertux_wrapper(global_vm);
86
87   // TODO remove this at some point... it shoud just be functions not an object
88   expose_object(global_vm, -1, new Scripting::Level(), "Level", true);
89   
90   sq_pop(global_vm, 1);
91
92   // register print function
93   sq_setprintfunc(global_vm, printfunc);
94   // register default error handlers
95   sqstd_seterrorhandlers(global_vm);
96
97   // try to load default script
98   try {
99     std::string filename = "scripts/default.nut";
100     IFileStream stream(filename);
101     Scripting::compile_and_run(global_vm, stream, filename);
102   } catch(std::exception& e) {
103     log_warning << "Couldn't load default.nut: " << e.what() << std::endl;
104   }
105 }
106
107 void exit_squirrel()
108 {
109 #ifdef ENABLE_SQDBG
110   if(debugger != NULL) {
111     sq_rdbg_shutdown(debugger);
112     debugger = NULL;
113   }
114 #endif
115
116   if (global_vm)
117     sq_close(global_vm);
118
119   global_vm = NULL;
120 }
121
122 void update_debugger()
123 {
124 #ifdef ENABLE_SQDBG
125   if(debugger != NULL)
126     sq_rdbg_update(debugger);
127 #endif
128 }
129
130 std::string squirrel2string(HSQUIRRELVM v, int i)
131 {
132   std::ostringstream os;
133   switch(sq_gettype(v, i))
134     {
135     case OT_NULL:
136       os << "<null>";        
137       break;
138     case OT_BOOL: {
139       SQBool p;
140       sq_getbool(v, i, &p);
141       if (p) 
142         os << "true";
143       else
144         os << "false";
145       break;
146     }
147     case OT_INTEGER: {
148       int val;
149       sq_getinteger(v, i, &val);
150       os << val;
151       break;
152     }
153     case OT_FLOAT: {
154       float val;
155       sq_getfloat(v, i, &val);
156       os << val;
157       break;
158     }
159     case OT_STRING: {
160       const char* val;
161       sq_getstring(v, i, &val);
162       os << "\"" << val << "\"";
163       break;    
164     }
165     case OT_TABLE: {
166       bool first = true;
167       os << "{";
168       sq_pushnull(v);  //null iterator
169       while(SQ_SUCCEEDED(sq_next(v,i-1)))
170         {
171           if (!first) {
172             os << ", ";
173           }
174           first = false;
175
176           //here -1 is the value and -2 is the key
177           os << squirrel2string(v, -2) << " => " 
178              << squirrel2string(v, -1);
179                               
180           sq_pop(v,2); //pops key and val before the nex iteration
181         }
182       sq_pop(v, 1);
183       os << "}";
184       break;
185     }
186     case OT_ARRAY: {
187       bool first = true;
188       os << "[";
189       sq_pushnull(v);  //null iterator
190       while(SQ_SUCCEEDED(sq_next(v,i-1)))
191         {
192           if (!first) {
193             os << ", ";
194           }
195           first = false;
196
197           //here -1 is the value and -2 is the key
198           // we ignore the key, since that is just the index in an array
199           os << squirrel2string(v, -1);
200                               
201           sq_pop(v,2); //pops key and val before the nex iteration
202         }
203       sq_pop(v, 1);
204       os << "]";
205       break;
206     }
207     case OT_USERDATA:
208       os << "<userdata>";
209       break;
210     case OT_CLOSURE:        
211       os << "<closure>";
212       break;
213     case OT_NATIVECLOSURE:
214       os << "<native closure>";
215       break;
216     case OT_GENERATOR:
217       os << "<generator>";
218       break;
219     case OT_USERPOINTER:
220       os << "userpointer";
221       break;
222     case OT_THREAD:
223       os << "<thread>";
224       break;
225     case OT_CLASS:
226       os << "<class>";
227       break;
228     case OT_INSTANCE:
229       os << "<instance>";
230       break;
231     case OT_WEAKREF:
232       os << "<weakref>";
233       break;
234     default:
235       os << "<unknown>";
236       break;
237     }
238   return os.str();
239 }
240
241 void print_squirrel_stack(HSQUIRRELVM v)
242 {
243     printf("--------------------------------------------------------------\n");
244     int count = sq_gettop(v);
245     for(int i = 1; i <= count; ++i) {
246         printf("%d: ",i);
247         switch(sq_gettype(v, i))
248         {
249             case OT_NULL:
250                 printf("null");        
251                 break;
252             case OT_INTEGER: {
253                 int val;
254                 sq_getinteger(v, i, &val);
255                 printf("integer (%d)", val);
256                 break;
257             }
258             case OT_FLOAT: {
259                 float val;
260                 sq_getfloat(v, i, &val);
261                 printf("float (%f)", val);
262                 break;
263             }
264             case OT_STRING: {
265                 const char* val;
266                 sq_getstring(v, i, &val);
267                 printf("string (%s)", val);
268                 break;    
269             }
270             case OT_TABLE:
271                 printf("table");
272                 break;
273             case OT_ARRAY:
274                 printf("array");
275                 break;
276             case OT_USERDATA:
277                 printf("userdata");
278                 break;
279             case OT_CLOSURE:        
280                 printf("closure(function)");    
281                 break;
282             case OT_NATIVECLOSURE:
283                 printf("native closure(C function)");
284                 break;
285             case OT_GENERATOR:
286                 printf("generator");
287                 break;
288             case OT_USERPOINTER:
289                 printf("userpointer");
290                 break;
291             case OT_THREAD:
292                 printf("thread");
293                 break;
294             case OT_CLASS:
295                 printf("class");
296                 break;
297             case OT_INSTANCE:
298                 printf("instance");
299                 break;
300             case OT_WEAKREF:
301                 printf("weakref");
302                 break;
303             default:
304                 printf("unknown?!?");
305                 break;
306         }
307         printf("\n");
308     }
309     printf("--------------------------------------------------------------\n");
310 }
311
312 static SQInteger squirrel_read_char(SQUserPointer file)
313 {
314   std::istream* in = reinterpret_cast<std::istream*> (file);
315   char c = in->get();
316   if(in->eof())
317     return 0;
318   return c;
319 }
320
321 void compile_script(HSQUIRRELVM vm, std::istream& in, const std::string& sourcename)
322 {
323   if(SQ_FAILED(sq_compile(vm, squirrel_read_char, &in, sourcename.c_str(), true)))
324     throw SquirrelError(vm, "Couldn't parse script");  
325 }
326
327 void compile_and_run(HSQUIRRELVM vm, std::istream& in,
328                      const std::string& sourcename)
329 {
330   compile_script(vm, in, sourcename);
331   
332   int oldtop = sq_gettop(vm);
333
334   try {
335     sq_pushroottable(vm);
336     if(SQ_FAILED(sq_call(vm, 1, SQFalse, SQTrue)))
337       throw SquirrelError(vm, "Couldn't start script");
338   } catch(...) {
339     sq_settop(vm, oldtop);
340     throw;
341   }
342
343   // we can remove the closure in case the script was not suspended
344   if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
345     sq_settop(vm, oldtop-1);
346   }
347 }
348
349 HSQOBJECT create_thread(HSQUIRRELVM vm)
350 {
351   HSQUIRRELVM new_vm = sq_newthread(vm, 64);
352   if(new_vm == NULL)
353     throw SquirrelError(vm, "Couldn't create new VM");
354
355   HSQOBJECT vm_object;
356   sq_resetobject(&vm_object);
357   if(SQ_FAILED(sq_getstackobj(vm, -1, &vm_object)))
358     throw SquirrelError(vm, "Couldn't get squirrel thread from stack");
359   sq_addref(vm, &vm_object);
360   
361   sq_pop(vm, 1);
362
363   return vm_object;
364 }
365
366 HSQOBJECT vm_to_object(HSQUIRRELVM vm)
367 {
368   HSQOBJECT object;
369   sq_resetobject(&object);
370   object._unVal.pThread = vm;
371   object._type = OT_THREAD;
372
373   return object;
374 }
375
376 HSQUIRRELVM object_to_vm(HSQOBJECT object)
377 {
378   if(object._type != OT_THREAD)
379     return NULL;
380
381   return object._unVal.pThread;
382 }
383
384 }