- More work on scripting interface
[supertux.git] / src / squirrel / sqstdlib / sqstdstream.cpp
1 /* see copyright notice in squirrel.h */\r
2 #include <new>\r
3 #include <stdio.h>\r
4 #include <stdlib.h>\r
5 #include <string.h>\r
6 #include <squirrel.h>\r
7 #include <sqstdio.h>\r
8 #include <sqstdblob.h>\r
9 #include "sqstdstream.h"\r
10 #include "sqstdblobimpl.h"\r
11 \r
12 #define SETUP_STREAM(v) \\r
13         SQStream *self = NULL; \\r
14         if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,SQSTD_STREAM_TYPE_TAG))) \\r
15                 return sq_throwerror(v,_SC("invalid type tag")); \\r
16         if(!self->IsValid())  \\r
17                 return sq_throwerror(v,_SC("the stream is invalid"));\r
18 \r
19 int _stream_readstr(HSQUIRRELVM v)\r
20 {\r
21     SETUP_STREAM(v);\r
22         SQInteger type = _SC('a'), size = 0;\r
23         sq_getinteger(v, 2, &size);\r
24         if(size <= 0) return sq_throwerror(v,_SC("invalid size"));\r
25         if(sq_gettop(v) > 2)\r
26                 sq_getinteger(v, 3, &type);\r
27         SQChar *dest = NULL;\r
28         switch(type) {\r
29         case _SC('a'): {\r
30                 char *temp;\r
31                 if(self->Read(sq_getscratchpad(v, size+1), size) != size)\r
32                         return sq_throwerror(v, _SC("io failure"));\r
33 #ifdef _UNICODE\r
34                 temp = (char*) sq_getscratchpad(v, size + (size * sizeof(SQChar)));\r
35                 dest = (SQChar*) &temp[size];\r
36                 size = (SQInteger)mbstowcs(dest, (const char*)temp, size);\r
37 #else\r
38                 temp = (char *) sq_getscratchpad(v, -1);\r
39                 dest = temp;\r
40 #endif\r
41                                    }\r
42                 break;\r
43         case _SC('u'): {\r
44                 wchar_t *temp;\r
45                 if(self->Read(sq_getscratchpad(v, (size + 1) * sizeof(wchar_t)),size * sizeof(wchar_t)) != (size * sizeof(wchar_t)))\r
46                         return sq_throwerror(v, _SC("io failure"));\r
47                 \r
48 #ifdef _UNICODE\r
49                 temp = (wchar_t*) sq_getscratchpad(v, -1);\r
50                 dest = (SQChar*) temp;\r
51 #else\r
52                 temp = (wchar_t*) sq_getscratchpad(v,(size * 3) + (size * sizeof(wchar_t)));\r
53                 dest = (char*) &temp[size];\r
54                 size = (SQInteger)wcstombs(dest, (const wchar_t*)temp, size);\r
55 #endif\r
56                                    }\r
57                 break;\r
58         default:\r
59                 return sq_throwerror(v, _SC("invalid coding"));\r
60         }\r
61 \r
62         sq_pushstring(v, dest, size);\r
63         return 1;\r
64 }\r
65 \r
66 int _stream_readblob(HSQUIRRELVM v)\r
67 {\r
68         SETUP_STREAM(v);\r
69         SQUserPointer data,blobp;\r
70         SQInteger size,res;\r
71         sq_getinteger(v,2,&size);\r
72         if(size > self->Len()) {\r
73                 size = self->Len();\r
74         }\r
75         data = sq_getscratchpad(v,size);\r
76         res = self->Read(data,size);\r
77         if(res <= 0)\r
78                 return sq_throwerror(v,_SC("no data left to read"));\r
79         blobp = sqstd_createblob(v,res);\r
80         memcpy(blobp,data,res);\r
81         return 1;\r
82 }\r
83 \r
84 #define SAFE_READN(ptr,len) { \\r
85         if(self->Read(ptr,len) != len) return sq_throwerror(v,_SC("io error")); \\r
86         }\r
87 int _stream_readn(HSQUIRRELVM v)\r
88 {\r
89         SETUP_STREAM(v);\r
90         SQInteger format;\r
91         sq_getinteger(v, 2, &format);\r
92         switch(format) {\r
93         case 'i': {\r
94                 int i;\r
95                 SAFE_READN(&i, sizeof(i));\r
96                 sq_pushinteger(v, i);\r
97                           }\r
98                 break;\r
99         case 's': {\r
100                 short s;\r
101                 SAFE_READN(&s, sizeof(short));\r
102                 sq_pushinteger(v, s);\r
103                           }\r
104                 break;\r
105         case 'w': {\r
106                 unsigned short w;\r
107                 SAFE_READN(&w, sizeof(unsigned short));\r
108                 sq_pushinteger(v, w);\r
109                           }\r
110                 break;\r
111         case 'c': {\r
112                 char c;\r
113                 SAFE_READN(&c, sizeof(char));\r
114                 sq_pushinteger(v, c);\r
115                           }\r
116                 break;\r
117         case 'b': {\r
118                 unsigned char c;\r
119                 SAFE_READN(&c, sizeof(unsigned char));\r
120                 sq_pushinteger(v, c);\r
121                           }\r
122                 break;\r
123         case 'f': {\r
124                 float f;\r
125                 SAFE_READN(&f, sizeof(float));\r
126                 sq_pushfloat(v, f);\r
127                           }\r
128                 break;\r
129         case 'd': {\r
130                 double d;\r
131                 SAFE_READN(&d, sizeof(double));\r
132                 sq_pushfloat(v, (SQFloat)d);\r
133                           }\r
134                 break;\r
135         default:\r
136                 return sq_throwerror(v, _SC("invalid format"));\r
137         }\r
138         return 1;\r
139 }\r
140 \r
141 int _stream_writestr(HSQUIRRELVM v)\r
142 {\r
143         SETUP_STREAM(v);\r
144         const SQChar *str,*res;\r
145         SQInteger trgformat = 'a',len = 0;\r
146         sq_getstring(v,2,&str);\r
147         len = sq_getsize(v,2);\r
148         if(sq_gettop(v)>2)\r
149                 sq_getinteger(v,3,&trgformat);\r
150         switch(trgformat)\r
151         {\r
152         case 'a':\r
153 #ifdef _UNICODE\r
154                 res = sq_getscratchpad(v,len*3);\r
155                 len = (SQInteger) wcstombs((char *)res, (const wchar_t*)str, len);\r
156 #else\r
157                 res = str;\r
158 #endif\r
159                 self->Write((void *)res,len);\r
160                 break;\r
161         case 'u':\r
162 #ifdef _UNICODE\r
163                 res = str;\r
164 #else\r
165                 res = sq_getscratchpad(v,len*sizeof(wchar_t));\r
166                 len = (SQInteger) mbstowcs((wchar_t*)res, str, len);\r
167 #endif\r
168                 self->Write((void *)res,len*sizeof(wchar_t));\r
169                 break;\r
170         default:\r
171                 return sq_throwerror(v,_SC("wrong encoding"));\r
172         }\r
173         \r
174         return 0;\r
175 }\r
176 \r
177 int _stream_writeblob(HSQUIRRELVM v)\r
178 {\r
179         SQUserPointer data;\r
180         int size;\r
181         SETUP_STREAM(v);\r
182         if(SQ_FAILED(sqstd_getblob(v,2,&data)))\r
183                 return sq_throwerror(v,_SC("invalid parameter"));\r
184         size = sqstd_getblobsize(v,2);\r
185         if(self->Write(data,size) != size)\r
186                 return sq_throwerror(v,_SC("io error"));\r
187         sq_pushinteger(v,size);\r
188         return 1;\r
189 }\r
190 \r
191 int _stream_writen(HSQUIRRELVM v)\r
192 {\r
193         SETUP_STREAM(v);\r
194         SQInteger format, ti;\r
195         SQFloat tf;\r
196         sq_getinteger(v, 3, &format);\r
197         switch(format) {\r
198         case 'i': {\r
199                 int i;\r
200                 sq_getinteger(v, 2, &ti);\r
201                 i = ti;\r
202                 self->Write(&i, sizeof(int));\r
203                           }\r
204                 break;\r
205         case 's': {\r
206                 short s;\r
207                 sq_getinteger(v, 2, &ti);\r
208                 s = ti;\r
209                 self->Write(&s, sizeof(short));\r
210                           }\r
211                 break;\r
212         case 'w': {\r
213                 unsigned short w;\r
214                 sq_getinteger(v, 2, &ti);\r
215                 w = ti;\r
216                 self->Write(&w, sizeof(unsigned short));\r
217                           }\r
218                 break;\r
219         case 'c': {\r
220                 char c;\r
221                 sq_getinteger(v, 2, &ti);\r
222                 c = ti;\r
223                 self->Write(&c, sizeof(char));\r
224                                   }\r
225                 break;\r
226         case 'b': {\r
227                 unsigned char b;\r
228                 sq_getinteger(v, 2, &ti);\r
229                 b = ti;\r
230                 self->Write(&b, sizeof(unsigned char));\r
231                           }\r
232                 break;\r
233         case 'f': {\r
234                 float f;\r
235                 sq_getfloat(v, 2, &tf);\r
236                 f = tf;\r
237                 self->Write(&f, sizeof(float));\r
238                           }\r
239                 break;\r
240         case 'd': {\r
241                 double d;\r
242                 sq_getfloat(v, 2, &tf);\r
243                 d = tf;\r
244                 self->Write(&d, sizeof(double));\r
245                           }\r
246                 break;\r
247         default:\r
248                 return sq_throwerror(v, _SC("invalid format"));\r
249         }\r
250         return 0;\r
251 }\r
252 \r
253 int _stream_seek(HSQUIRRELVM v)\r
254 {\r
255         SETUP_STREAM(v);\r
256         SQInteger offset, origin = SQ_SEEK_SET;\r
257         sq_getinteger(v, 2, &offset);\r
258         if(sq_gettop(v) > 2) {\r
259                 SQInteger t;\r
260                 sq_getinteger(v, 3, &t);\r
261                 switch(t) {\r
262                         case 'b': origin = SQ_SEEK_SET; break;\r
263                         case 'c': origin = SQ_SEEK_CUR; break;\r
264                         case 'e': origin = SQ_SEEK_END; break;\r
265                         default: return sq_throwerror(v,_SC("invalid origin"));\r
266                 }\r
267         }\r
268         sq_pushinteger(v, self->Seek(offset, origin));\r
269         return 1;\r
270 }\r
271 \r
272 int _stream_tell(HSQUIRRELVM v)\r
273 {\r
274         SETUP_STREAM(v);\r
275         sq_pushinteger(v, self->Tell());\r
276         return 1;\r
277 }\r
278 \r
279 int _stream_len(HSQUIRRELVM v)\r
280 {\r
281         SETUP_STREAM(v);\r
282         sq_pushinteger(v, self->Len());\r
283         return 1;\r
284 }\r
285 \r
286 int _stream_flush(HSQUIRRELVM v)\r
287 {\r
288         SETUP_STREAM(v);\r
289         if(!self->Flush())\r
290                 sq_pushinteger(v, 1);\r
291         else\r
292                 sq_pushnull(v);\r
293         return 1;\r
294 }\r
295 \r
296 int _stream_eos(HSQUIRRELVM v)\r
297 {\r
298         SETUP_STREAM(v);\r
299         if(self->EOS())\r
300                 sq_pushinteger(v, 1);\r
301         else\r
302                 sq_pushnull(v);\r
303         return 1;\r
304 }\r
305 \r
306 static SQRegFunction _stream_methods[] = {\r
307         _DECL_STREAM_FUNC(readstr,-2,_SC("xnn")),\r
308         _DECL_STREAM_FUNC(readblob,2,_SC("xn")),\r
309         _DECL_STREAM_FUNC(readn,2,_SC("xn")),\r
310         _DECL_STREAM_FUNC(writestr,-2,_SC("xsn")),\r
311         _DECL_STREAM_FUNC(writeblob,-2,_SC("xx")),\r
312         _DECL_STREAM_FUNC(writen,3,_SC("xnn")),\r
313         _DECL_STREAM_FUNC(seek,-2,_SC("xnn")),\r
314         _DECL_STREAM_FUNC(tell,1,_SC("x")),\r
315         _DECL_STREAM_FUNC(len,1,_SC("x")),\r
316         _DECL_STREAM_FUNC(eos,1,_SC("x")),\r
317         _DECL_STREAM_FUNC(flush,1,_SC("x")),\r
318         {0,0}\r
319 };\r
320 \r
321 void init_streamclass(HSQUIRRELVM v)\r
322 {\r
323         sq_pushregistrytable(v);\r
324         sq_pushstring(v,_SC("std_stream"),-1);\r
325         if(SQ_FAILED(sq_get(v,-2))) {\r
326                 sq_pushstring(v,_SC("std_stream"),-1);\r
327                 sq_newclass(v,SQFalse);\r
328                 sq_settypetag(v,-1,SQSTD_STREAM_TYPE_TAG);\r
329                 int i = 0;\r
330                 while(_stream_methods[i].name != 0) {\r
331                         SQRegFunction &f = _stream_methods[i];\r
332                         sq_pushstring(v,f.name,-1);\r
333                         sq_newclosure(v,f.f,0);\r
334                         sq_setparamscheck(v,f.nparamscheck,f.typemask);\r
335                         sq_createslot(v,-3);\r
336                         i++;\r
337                 }\r
338                 sq_createslot(v,-3);\r
339         }\r
340         else {\r
341                 sq_pop(v,1); //result\r
342         }\r
343         sq_pop(v,1);\r
344 }\r
345 \r
346 SQRESULT declare_stream(HSQUIRRELVM v,SQChar* name,int typetag,SQChar* reg_name,SQRegFunction *methods,SQRegFunction *globals)\r
347 {\r
348         if(sq_gettype(v,-1) != OT_TABLE)\r
349                 return sq_throwerror(v,_SC("table expected"));\r
350         int top = sq_gettop(v);\r
351         //create delegate\r
352     init_streamclass(v);\r
353         sq_pushregistrytable(v);\r
354         sq_pushstring(v,reg_name,-1);\r
355         sq_pushstring(v,_SC("std_stream"),-1);\r
356         if(SQ_SUCCEEDED(sq_get(v,-3))) {\r
357                 sq_newclass(v,SQTrue);\r
358                 sq_settypetag(v,-1,typetag);\r
359                 int i = 0;\r
360                 while(methods[i].name != 0) {\r
361                         SQRegFunction &f = methods[i];\r
362                         sq_pushstring(v,f.name,-1);\r
363                         sq_newclosure(v,f.f,0);\r
364                         sq_setparamscheck(v,f.nparamscheck,f.typemask);\r
365                         sq_setnativeclosurename(v,-1,f.name);\r
366                         sq_createslot(v,-3);\r
367                         i++;\r
368                 }\r
369                 sq_createslot(v,-3);\r
370                 sq_pop(v,1);\r
371                 \r
372                 i = 0;\r
373                 while(globals[i].name!=0)\r
374                 {\r
375                         SQRegFunction &f = globals[i];\r
376                         sq_pushstring(v,f.name,-1);\r
377                         sq_newclosure(v,f.f,0);\r
378                         sq_setparamscheck(v,f.nparamscheck,f.typemask);\r
379                         sq_setnativeclosurename(v,-1,f.name);\r
380                         sq_createslot(v,-3);\r
381                         i++;\r
382                 }\r
383                 //register the class in the target table\r
384                 sq_pushstring(v,name,-1);\r
385                 sq_pushregistrytable(v);\r
386                 sq_pushstring(v,reg_name,-1);\r
387                 sq_get(v,-2);\r
388                 sq_remove(v,-2);\r
389                 sq_createslot(v,-3);\r
390 \r
391                 sq_settop(v,top);\r
392                 return SQ_OK;\r
393         }\r
394         sq_settop(v,top);\r
395         return SQ_ERROR;\r
396 }\r