New grow and skid sounds from remaxim
[supertux.git] / src / squirrel / sqstdlib / sqstdio.cpp
1 /* see copyright notice in squirrel.h */
2 #include <new>
3 #include <stdio.h>
4 #include <squirrel.h>
5 #include <sqstdio.h>
6 #include "sqstdstream.h"
7
8 #define SQSTD_FILE_TYPE_TAG (SQSTD_STREAM_TYPE_TAG | 0x00000001)
9 //basic API
10 SQFILE sqstd_fopen(const SQChar *filename ,const SQChar *mode)
11 {
12 #ifndef SQUNICODE
13         return (SQFILE)fopen(filename,mode);
14 #else
15         return (SQFILE)_wfopen(filename,mode);
16 #endif
17 }
18
19 SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file)
20 {
21         return (SQInteger)fread(buffer,size,count,(FILE *)file);
22 }
23
24 SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file)
25 {
26         return (SQInteger)fwrite(buffer,size,count,(FILE *)file);
27 }
28
29 SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin)
30 {
31         SQInteger realorigin;
32         switch(origin) {
33                 case SQ_SEEK_CUR: realorigin = SEEK_CUR; break;
34                 case SQ_SEEK_END: realorigin = SEEK_END; break;
35                 case SQ_SEEK_SET: realorigin = SEEK_SET; break;
36                 default: return -1; //failed
37         }
38         return fseek((FILE *)file,(long)offset,(int)realorigin);
39 }
40
41 SQInteger sqstd_ftell(SQFILE file)
42 {
43         return ftell((FILE *)file);
44 }
45
46 SQInteger sqstd_fflush(SQFILE file)
47 {
48         return fflush((FILE *)file);
49 }
50
51 SQInteger sqstd_fclose(SQFILE file)
52 {
53         return fclose((FILE *)file);
54 }
55
56 SQInteger sqstd_feof(SQFILE file)
57 {
58         return feof((FILE *)file);
59 }
60
61 //File
62 struct SQFile : public SQStream {
63         SQFile() { _handle = NULL; _owns = false;}
64         SQFile(SQFILE file, bool owns) { _handle = file; _owns = owns;}
65         virtual ~SQFile() { Close(); }
66         bool Open(const SQChar *filename ,const SQChar *mode) {
67                 Close();
68                 if( (_handle = sqstd_fopen(filename,mode)) ) {
69                         _owns = true;
70                         return true;
71                 }
72                 return false;
73         }
74         void Close() {
75                 if(_handle && _owns) { 
76                         sqstd_fclose(_handle);
77                         _handle = NULL;
78                         _owns = false;
79                 }
80         }
81         SQInteger Read(void *buffer,SQInteger size) {
82                 return sqstd_fread(buffer,1,size,_handle);
83         }
84         SQInteger Write(void *buffer,SQInteger size) {
85                 return sqstd_fwrite(buffer,1,size,_handle);
86         }
87         SQInteger Flush() {
88                 return sqstd_fflush(_handle);
89         }
90         SQInteger Tell() {
91                 return sqstd_ftell(_handle);
92         }
93         SQInteger Len() {
94                 SQInteger prevpos=Tell();
95                 Seek(0,SQ_SEEK_END);
96                 SQInteger size=Tell();
97                 Seek(prevpos,SQ_SEEK_SET);
98                 return size;
99         }
100         SQInteger Seek(SQInteger offset, SQInteger origin)      {
101                 return sqstd_fseek(_handle,offset,origin);
102         }
103         bool IsValid() { return _handle?true:false; }
104         bool EOS() { return Tell()==Len()?true:false;}
105         SQFILE GetHandle() {return _handle;}
106 private:
107         SQFILE _handle;
108         bool _owns;
109 };
110
111 static SQInteger _file__typeof(HSQUIRRELVM v)
112 {
113         sq_pushstring(v,_SC("file"),-1);
114         return 1;
115 }
116
117 static SQInteger _file_releasehook(SQUserPointer p, SQInteger size)
118 {
119         SQFile *self = (SQFile*)p;
120         delete self;
121         return 1;
122 }
123
124 static SQInteger _file_constructor(HSQUIRRELVM v)
125 {
126         const SQChar *filename,*mode;
127         bool owns = true;
128         SQFile *f;
129         SQFILE newf;
130         if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) {
131                 sq_getstring(v, 2, &filename);
132                 sq_getstring(v, 3, &mode);
133                 newf = sqstd_fopen(filename, mode);
134                 if(!newf) return sq_throwerror(v, _SC("cannot open file"));
135         } else if(sq_gettype(v,2) == OT_USERPOINTER) {
136                 owns = !(sq_gettype(v,3) == OT_NULL);
137                 sq_getuserpointer(v,2,&newf);
138         } else {
139                 return sq_throwerror(v,_SC("wrong parameter"));
140         }
141         f = new SQFile(newf,owns);
142         if(SQ_FAILED(sq_setinstanceup(v,1,f))) {
143                 delete f;
144                 return sq_throwerror(v, _SC("cannot create blob with negative size"));
145         }
146         sq_setreleasehook(v,1,_file_releasehook);
147         return 0;
148 }
149
150 //bindings
151 #define _DECL_FILE_FUNC(name,nparams,typecheck) {_SC(#name),_file_##name,nparams,typecheck}
152 static SQRegFunction _file_methods[] = {
153         _DECL_FILE_FUNC(constructor,3,_SC("x")),
154         _DECL_FILE_FUNC(_typeof,1,_SC("x")),
155         {0,0,0,0},
156 };
157
158
159
160 SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own)
161 {
162         SQInteger top = sq_gettop(v);
163         sq_pushregistrytable(v);
164         sq_pushstring(v,_SC("std_file"),-1);
165         if(SQ_SUCCEEDED(sq_get(v,-2))) {
166                 sq_remove(v,-2); //removes the registry
167                 sq_pushroottable(v); // push the this
168                 sq_pushuserpointer(v,file); //file
169                 if(own){
170                         sq_pushinteger(v,1); //true
171                 }
172                 else{
173                         sq_pushnull(v); //false
174                 }
175                 if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) {
176                         sq_remove(v,-2);
177                         return SQ_OK;
178                 }
179         }
180         sq_settop(v,top);
181         return SQ_OK;
182 }
183
184 SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file)
185 {
186         SQFile *fileobj = NULL;
187         if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) {
188                 *file = fileobj->GetHandle();
189                 return SQ_OK;
190         }
191         return sq_throwerror(v,_SC("not a file"));
192 }
193
194
195
196 static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file)
197 {
198         SQInteger ret;
199         char c;
200         if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
201                 return c;
202         return 0;
203 }
204
205 static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file)
206 {
207 #define READ() \
208         if(sqstd_fread(&inchar,sizeof(inchar),1,(FILE *)file) != 1) \
209                 return 0;
210
211         static const SQInteger utf8_lengths[16] =
212         {
213                 1,1,1,1,1,1,1,1,        /* 0000 to 0111 : 1 byte (plain ASCII) */
214                 0,0,0,0,                /* 1000 to 1011 : not valid */
215                 2,2,                    /* 1100, 1101 : 2 bytes */
216                 3,                      /* 1110 : 3 bytes */
217                 4                       /* 1111 :4 bytes */
218         };
219         static unsigned char byte_masks[5] = {0,0,0x1f,0x0f,0x07};
220         unsigned char inchar;
221         SQInteger c = 0;
222         READ();
223         c = inchar;
224         //
225         if(c >= 0x80) {
226                 SQInteger tmp;
227                 SQInteger codelen = utf8_lengths[c>>4];
228                 if(codelen == 0) 
229                         return 0;
230                         //"invalid UTF-8 stream";
231                 tmp = c&byte_masks[codelen];
232                 for(SQInteger n = 0; n < codelen-1; n++) {
233                         tmp<<=6;
234                         READ();
235                         tmp |= inchar & 0x3F;
236                 }
237                 c = tmp;
238         }
239         return c;
240 }
241
242 static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file)
243 {
244         SQInteger ret;
245         wchar_t c;
246         if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) )
247                 return (SQChar)c;
248         return 0;
249 }
250
251 static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file)
252 {
253         SQInteger ret;
254         unsigned short c;
255         if( ( ret=sqstd_fread(&c,sizeof(c),1,(FILE *)file )>0) ) {
256                 c = ((c>>8)&0x00FF)| ((c<<8)&0xFF00);
257                 return (SQChar)c;
258         }
259         return 0;
260 }
261
262 SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size)
263 {
264         SQInteger ret;
265         if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret;
266         return -1;
267 }
268
269 SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size)
270 {
271         return sqstd_fwrite(p,1,size,(SQFILE)file);
272 }
273
274 SQRESULT sqstd_loadfile(HSQUIRRELVM v,const SQChar *filename,SQBool printerror)
275 {
276         SQFILE file = sqstd_fopen(filename,_SC("rb"));
277         SQInteger ret;
278         unsigned short us;
279         unsigned char uc;
280         SQLEXREADFUNC func = _io_file_lexfeed_ASCII;
281         if(file){
282                 ret = sqstd_fread(&us,1,2,file);
283                 if(ret != 2) {
284                         //probably an empty file
285                         us = 0;
286                 }
287                 if(us == SQ_BYTECODE_STREAM_TAG) { //BYTECODE
288                         sqstd_fseek(file,0,SQ_SEEK_SET);
289                         if(SQ_SUCCEEDED(sq_readclosure(v,file_read,file))) {
290                                 sqstd_fclose(file);
291                                 return SQ_OK;
292                         }
293                 }
294                 else { //SCRIPT
295                         switch(us)
296                         {
297                                 //gotta swap the next 2 lines on BIG endian machines
298                                 case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;//UTF-16 little endian;
299                                 case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;//UTF-16 big endian;
300                                 case 0xBBEF: 
301                                         if(sqstd_fread(&uc,1,sizeof(uc),file) == 0) { 
302                                                 sqstd_fclose(file); 
303                                                 return sq_throwerror(v,_SC("io error")); 
304                                         }
305                                         if(uc != 0xBF) { 
306                                                 sqstd_fclose(file); 
307                                                 return sq_throwerror(v,_SC("Unrecognozed ecoding")); 
308                                         }
309                                         func = _io_file_lexfeed_UTF8;
310                                         break;//UTF-8 ;
311                                 default: sqstd_fseek(file,0,SQ_SEEK_SET); break; // ascii
312                         }
313
314                         if(SQ_SUCCEEDED(sq_compile(v,func,file,filename,printerror))){
315                                 sqstd_fclose(file);
316                                 return SQ_OK;
317                         }
318                 }
319                 sqstd_fclose(file);
320                 return SQ_ERROR;
321         }
322         return sq_throwerror(v,_SC("cannot open the file"));
323 }
324
325 SQRESULT sqstd_dofile(HSQUIRRELVM v,const SQChar *filename,SQBool retval,SQBool printerror)
326 {
327         if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) {
328                 sq_push(v,-2);
329                 if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) {
330                         sq_remove(v,retval?-2:-1); //removes the closure
331                         return 1;
332                 }
333                 sq_pop(v,1); //removes the closure
334         }
335         return SQ_ERROR;
336 }
337
338 SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const SQChar *filename)
339 {
340         SQFILE file = sqstd_fopen(filename,_SC("wb+"));
341         if(!file) return sq_throwerror(v,_SC("cannot open the file"));
342         if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) {
343                 sqstd_fclose(file);
344                 return SQ_OK;
345         }
346         sqstd_fclose(file);
347         return SQ_ERROR; //forward the error
348 }
349
350 SQInteger _g_io_loadfile(HSQUIRRELVM v)
351 {
352         const SQChar *filename;
353         SQBool printerror = SQFalse;
354         sq_getstring(v,2,&filename);
355         if(sq_gettop(v) >= 3) {
356                 sq_getbool(v,3,&printerror);
357         }
358         if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror)))
359                 return 1;
360         return SQ_ERROR; //propagates the error
361 }
362
363 SQInteger _g_io_writeclosuretofile(HSQUIRRELVM v)
364 {
365         const SQChar *filename;
366         sq_getstring(v,2,&filename);
367         if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,filename)))
368                 return 1;
369         return SQ_ERROR; //propagates the error
370 }
371
372 SQInteger _g_io_dofile(HSQUIRRELVM v)
373 {
374         const SQChar *filename;
375         SQBool printerror = SQFalse;
376         sq_getstring(v,2,&filename);
377         if(sq_gettop(v) >= 3) {
378                 sq_getbool(v,3,&printerror);
379         }
380         sq_push(v,1); //repush the this
381         if(SQ_SUCCEEDED(sqstd_dofile(v,filename,SQTrue,printerror)))
382                 return 1;
383         return SQ_ERROR; //propagates the error
384 }
385
386 #define _DECL_GLOBALIO_FUNC(name,nparams,typecheck) {_SC(#name),_g_io_##name,nparams,typecheck}
387 static SQRegFunction iolib_funcs[]={
388         _DECL_GLOBALIO_FUNC(loadfile,-2,_SC(".sb")),
389         _DECL_GLOBALIO_FUNC(dofile,-2,_SC(".sb")),
390         _DECL_GLOBALIO_FUNC(writeclosuretofile,3,_SC(".sc")),
391         {0,0}
392 };
393
394 SQRESULT sqstd_register_iolib(HSQUIRRELVM v)
395 {
396         SQInteger top = sq_gettop(v);
397         //create delegate
398         declare_stream(v,_SC("file"),(SQUserPointer)SQSTD_FILE_TYPE_TAG,_SC("std_file"),_file_methods,iolib_funcs);
399         sq_pushstring(v,_SC("stdout"),-1);
400         sqstd_createfile(v,stdout,SQFalse);
401         sq_createslot(v,-3);
402         sq_pushstring(v,_SC("stdin"),-1);
403         sqstd_createfile(v,stdin,SQFalse);
404         sq_createslot(v,-3);
405         sq_pushstring(v,_SC("stderr"),-1);
406         sqstd_createfile(v,stderr,SQFalse);
407         sq_createslot(v,-3);
408         sq_settop(v,top);
409         return SQ_OK;
410 }