added support for load(data=...)
[swftools.git] / lib / python / SWF.c
1 /* SWF.c
2
3    Python wrapper for librfxswf- module core.
4
5    Part of the swftools package.
6
7    Copyright (c) 2003 Matthias Kramm <kramm@quiss.org>
8  
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
22
23 #include <Python.h>
24 #undef HAVE_STAT
25 #include "../rfxswf.h"
26 #include "../log.h"
27 #include "./pyutils.h"
28 #include "./tags.h"
29 #include "./taglist.h"
30 #include "./primitives.h"
31 #include "./action.h"
32
33 /*
34 TODO:
35     1) taglist is rfxswflib's linked list. It should maybe implemented as Python
36        list, which would, however, mean that we would have to convert the list
37        back and forth for the following functions:
38         load, save, writeCGI, unfoldAll, foldAll, optimizeOrder
39     2) taglist should have an ID handler. Every time a tag is inserted, it's ID
40        is stored in a lookup list.
41     3) 
42 */
43
44 //-------------------------- Types -------------------------------------------
45
46 staticforward PyTypeObject SWFClass;
47
48 /* Tags, Objects */
49
50 typedef struct {
51     PyObject_HEAD
52     SWF swf; //swf.firstTag is not used
53     PyObject*taglist;
54     char*filename;
55 } SWFObject;
56
57
58 //----------------------------------------------------------------------------
59 static PyObject* f_create(PyObject* self, PyObject* args, PyObject* kwargs)
60 {
61     static char *kwlist[] = {"version", "fps", "bbox", "name", NULL};
62     SWFObject* swf;
63     int version = 6;
64     double framerate = 25;
65     PyObject * obbox = 0;
66     SRECT bbox = {0,0,0,0};
67     char* filename = 0;
68     
69     swf = PyObject_New(SWFObject, &SWFClass);
70     mylog("+%08x(%d) create\n", (int)swf, swf->ob_refcnt);
71
72     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|idOs", 
73                 kwlist, &version, &framerate, 
74                 &obbox, &filename))
75         return NULL;
76
77     if(obbox) {
78         if (!PY_CHECK_TYPE(obbox, &BBoxClass)) {
79             obbox = f_BBox(0, obbox, 0);
80             if(!obbox)
81                 return NULL;
82         }
83         bbox = bbox_getSRECT(obbox);
84     }
85
86     memset(&swf->swf, 0, sizeof(SWF));
87     if(filename)
88         swf->filename = strdup(filename);
89     else
90         swf->filename = 0;
91
92     swf->swf.fileVersion = version;
93     swf->swf.frameRate = (int)(framerate*0x100);
94     swf->swf.movieSize = bbox;
95     swf->taglist = taglist_new();
96
97     if(swf->swf.fileVersion>=6)
98         swf->swf.compressed = 1;
99
100     mylog(" %08x(%d) create: done\n", (int)swf, swf->ob_refcnt);
101     return (PyObject*)swf;
102 }
103 //----------------------------------------------------------------------------
104 static PyObject* f_load(PyObject* self, PyObject* args, PyObject* kwargs)
105 {
106     static char *kwlist1[] = {"filename", NULL};
107     static char *kwlist2[] = {"data", NULL};
108     char* filename = 0;
109     char* data = 0;
110     int len = 0;
111     SWFObject* swf;
112     int fi;
113
114     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist1, &filename)) {
115         PyErr_Clear();
116         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#", kwlist2, &data, &len)) {
117             PyErr_Clear();
118             PyArg_ParseTupleAndKeywords(args, kwargs, "s:load", kwlist1, &filename);
119             return 0;
120         }
121     }
122
123     swf = PyObject_New(SWFObject, &SWFClass);
124     mylog("+%08x(%d) f_load\n", (int)swf, swf->ob_refcnt);
125
126     memset(&swf->swf, 0, sizeof(SWF));
127
128     if(filename) {
129         if(!filename) {
130             PyErr_SetString(PyExc_Exception, setError("Couldn't open file %s", filename));
131             return 0;
132         }
133         swf->filename = strdup(filename);
134         fi = open(filename,O_RDONLY|O_BINARY);
135         if (fi<0) { 
136             return PY_ERROR("Couldn't open file %s", filename);
137         }
138         if(swf_ReadSWF(fi,&swf->swf)<0) { 
139             close(fi);
140             return PY_ERROR("%s is not a valid SWF file or contains errors",filename);
141         }
142         close(fi);
143     } else {
144         struct reader_t r;
145         reader_init_memreader(&r, data, len);
146         swf->filename = 0;
147         if(swf_ReadSWF2(&r, &swf->swf)<0) {
148             return PY_ERROR("<data> is not a valid SWF file or contains errors");
149         }
150         r.dealloc(&r);
151     }
152     swf_FoldAll(&swf->swf);
153
154     swf->taglist = taglist_new2(swf->swf.firstTag);
155     if(swf->taglist == NULL) {
156         return NULL;
157     }
158     
159     swf_FreeTags(&swf->swf);
160     swf->swf.firstTag = 0;
161     
162     return (PyObject*)swf;
163 }
164 //----------------------------------------------------------------------------
165 static PyObject * swf_save(PyObject* self, PyObject* args, PyObject* kwargs)
166 {
167     static char *kwlist[] = {"name", "compress", NULL};
168     SWFObject*swfo;
169     SWF*swf;
170     int fi;
171     char*filename = 0;
172     int compress = 0;
173     
174     if(!self)
175         return NULL;
176
177     swfo = (SWFObject*)self;
178     swf = &swfo->swf;
179     
180     filename = swfo->filename;
181
182     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|si", kwlist, &filename, &compress))
183         return NULL;
184     
185     mylog(" %08x(%d) f_save filename=%s compress=%d\n", (int)self, self->ob_refcnt, filename, compress);
186
187     // keyword arg compress (=1) forces compression
188     if(compress)
189         swf->compressed = 1;
190     
191     swf->firstTag = taglist_getTAGs(swfo->taglist);
192
193     /*if(!swf->firstTag)
194         return NULL;*/
195
196     // fix the file, in case it is empty or not terminated properly
197     {
198         TAG*tag = swf->firstTag;
199         if(!tag)
200             tag = swf->firstTag = swf_InsertTag(0,ST_END);
201         while(tag && tag->next) {
202             tag = tag->next;
203         }
204         if(tag->id != ST_END) {
205             tag = swf_InsertTag(tag,ST_END);
206         }
207     }
208
209     fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
210     if(fi<0) {
211         PyErr_SetString(PyExc_Exception, setError("couldn't create output file %s", filename));
212         return 0;
213     }
214     if(swf->compressed) {
215             if(swf_WriteSWC(fi, swf)<0) {
216                 close(fi);
217                 PyErr_SetString(PyExc_Exception, setError("WriteSWC() failed."));
218                 return 0;
219             }
220     } else {
221             if(swf_WriteSWF(fi, swf)<0) {
222                 close(fi);
223                 PyErr_SetString(PyExc_Exception, setError("WriteSWC() failed."));
224                 return 0;
225             }
226     }
227     close(fi);
228
229     swf_FreeTags(swf);
230     /*{ TAG * t = swf->firstTag;
231       while (t)
232       { 
233         mylog("tag: %08x\n",t);
234         mylog("  id: %d (%s)\n", t->id, swf_TagGetName(t));
235         mylog("  data: %08x (%d bytes)\n", t->data, t->len);
236         mylog("  next: %08x\n", t->next);
237         TAG * tnew = t->next;
238         mylog("->free data\n");
239         if (t->data) free(t->data);
240         mylog("->free tag\n");
241         free(t);
242         t = tnew;
243       }
244     }*/
245     swf->firstTag = 0;
246     
247     mylog(" %08x(%d) f_save filename=%s done\n", (int)self, self->ob_refcnt, filename);
248     
249     return PY_NONE;
250 }
251 //----------------------------------------------------------------------------
252 static PyObject * swf_writeCGI(PyObject* self, PyObject* args)
253 {
254     SWFObject*swf = (SWFObject*)self;
255     if(!self || !PyArg_ParseTuple(args,"")) 
256         return NULL;
257     swf->swf.firstTag = taglist_getTAGs(swf->taglist);
258     if(!swf->swf.firstTag)
259         return NULL;
260     swf_WriteCGI(&swf->swf);
261     swf_FreeTags(&swf->swf);
262     swf->swf.firstTag = 0;
263     return PY_NONE;
264 }
265 //----------------------------------------------------------------------------
266
267 //TODO: void swf_Relocate(SWF*swf, char*bitmap); // bitmap is 65536 bytes, bitmap[a]==0 means id a is free
268
269 static PyMethodDef swf_functions[] =
270 {{"save", (PyCFunction)swf_save, METH_KEYWORDS, "Save SWF to disk"},
271  {"writeCGI", (PyCFunction)swf_writeCGI, METH_VARARGS, "print SWF as CGI to stdout"},
272  {NULL, NULL, 0, NULL}
273 };
274  
275 //----------------------------------------------------------------------------
276 static void swf_dealloc(PyObject* self)
277 {
278     mylog("-%08x(%d) swf_dealloc\n", (int)self, self->ob_refcnt);
279     SWFObject*swfo;
280     SWF*swf;
281     swfo = (SWFObject*)self;
282     swf = &swfo->swf;
283     if(swfo->filename) {
284         free(swfo->filename);
285         swfo->filename = 0;
286     }
287     Py_DECREF(swfo->taglist);
288     swfo->taglist = 0;
289     PyObject_Del(self);
290 }
291 //----------------------------------------------------------------------------
292 static int swf_print(PyObject * self, FILE *fi, int flags) //flags&Py_PRINT_RAW
293 {
294     mylog(" %08x(%d) print \n", (int)self, self->ob_refcnt);
295     SWFObject*swf = (SWFObject*)self;
296     swf_DumpHeader(fi, &swf->swf);
297     //void swf_DumpSWF(FILE * f,SWF*swf);
298     return 0;
299 }
300 //----------------------------------------------------------------------------
301 static PyObject* swf_getattr(PyObject * self, char* a)
302 {
303     SWFObject*swf = (SWFObject*)self;
304     PyObject* ret = 0;
305
306     if(!strcmp(a, "fps")) {
307         double fps = swf->swf.frameRate/256.0;
308         mylog(" %08x(%d) swf_getattr %s = %f\n", (int)self, self->ob_refcnt, a, fps);
309         return Py_BuildValue("d", fps);
310     } else if(!strcmp(a, "version")) {
311         int version = swf->swf.fileVersion;;
312         mylog(" %08x(%d) swf_getattr %s = %d\n", (int)self, self->ob_refcnt, a, version);
313         return Py_BuildValue("i", version);
314     } else if(!strcmp(a, "name")) {
315         char*filename = swf->filename;
316         mylog(" %08x(%d) swf_getattr %s = %s\n", (int)self, self->ob_refcnt, a, filename);
317         return Py_BuildValue("s", filename);
318     } else if(!strcmp(a, "bbox")) {
319         return f_BBox2(swf->swf.movieSize);
320     } else if(!strcmp(a, "tags")) {
321         PyObject*ret =  (PyObject*)(swf->taglist);
322         Py_INCREF(ret);
323         mylog(" %08x(%d) swf_getattr %s = %08x(%d)\n", (int)self, self->ob_refcnt, a, ret, ret->ob_refcnt);
324         return ret;
325     } else if(!strcmp(a, "filesize")) {
326         int s = swf->swf.fileSize;
327         return Py_BuildValue("i", s);
328     } else if(!strcmp(a, "width")) {
329         int w = (swf->swf.movieSize.xmax - swf->swf.movieSize.xmin) / 20;
330         return Py_BuildValue("i", w);
331     } else if(!strcmp(a, "height")) {
332         int h =  (swf->swf.movieSize.ymax - swf->swf.movieSize.ymin) / 20;
333         return Py_BuildValue("i", h);
334     } else if(!strcmp(a, "framecount")) {
335         int f = swf->swf.frameCount;
336         return Py_BuildValue("i", f);
337     }
338
339     ret = Py_FindMethod(swf_functions, self, a);
340     mylog(" %08x(%d) swf_getattr %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
341     return ret;
342 }
343 //----------------------------------------------------------------------------
344 static int swf_setattr(PyObject * self, char* a, PyObject * o)
345 {
346     SWFObject*swf = (SWFObject*)self;
347     if(!strcmp(a, "fps")) {
348         double fps;
349         if (!PyArg_Parse(o, "d", &fps)) 
350             goto err;
351         swf->swf.frameRate = (int)(fps*0x100);
352         mylog(" %08x(%d) swf_setattr %s = %f\n", (int)self, self->ob_refcnt, a, fps);
353         return 0;
354     } else if(!strcmp(a, "version")) {
355         int version;
356         if (!PyArg_Parse(o, "i", &version)) 
357             goto err;
358         swf->swf.fileVersion = version;
359         mylog(" %08x(%d) swf_setattr %s = %d\n", (int)self, self->ob_refcnt, a, version);
360         return 0;
361     } else if(!strcmp(a, "name")) {
362         char*filename;
363         if (!PyArg_Parse(o, "s", &filename)) 
364             goto err;
365         if(swf->filename) {
366             free(swf->filename);swf->filename=0;
367         }
368         swf->filename = strdup(filename);
369         mylog(" %08x(%d) swf_setattr %s = %s\n", (int)self, self->ob_refcnt, a, filename);
370         return 0;
371     } else if(!strcmp(a, "bbox")) {
372         PyObject *obbox = o;
373         if (!PY_CHECK_TYPE(obbox, &BBoxClass)) {
374             obbox = f_BBox(0, o, 0);
375             if(!obbox)
376                 return 1;
377         }
378         SRECT bbox = bbox_getSRECT(obbox);
379
380         swf->swf.movieSize = bbox;
381         mylog(" %08x(%d) swf_setattr %s = (%d,%d,%d,%d)\n", (int)self, self->ob_refcnt, a, bbox.xmin,bbox.ymin,bbox.xmax,bbox.ymax);
382         return 0;
383     } else if(!strcmp(a, "tags")) {
384         PyObject* taglist;
385         taglist = o;
386         PY_ASSERT_TYPE(taglist,&TagListClass);
387         Py_DECREF(swf->taglist);
388         swf->taglist = taglist;
389         Py_INCREF(swf->taglist);
390         mylog(" %08x(%d) swf_setattr %s = %08x\n", (int)self, self->ob_refcnt, a, swf->taglist);
391         return 0;
392     }
393 err:
394     mylog(" %08x(%d) swf_setattr %s = ? (%08x)\n", (int)self, self->ob_refcnt, a, o);
395     return 1;
396 }
397
398 //----------------------------------------------------------------------------
399 static PyTypeObject SWFClass = 
400 {
401     PyObject_HEAD_INIT(NULL)
402     0,
403     tp_name: "SWF",
404     tp_basicsize: sizeof(SWFObject),
405     tp_itemsize: 0,
406     tp_dealloc: swf_dealloc,
407     tp_print: swf_print,
408     tp_getattr: swf_getattr,
409     tp_setattr: swf_setattr,
410 };
411 //----------------------------------------------------------------------------
412
413 static PyMethodDef SWFMethods[] = 
414 {
415     /* SWF creation*/
416     {"load", (PyCFunction)f_load, METH_KEYWORDS, "Load a SWF from disc."},
417     {"create", (PyCFunction)f_create, METH_KEYWORDS, "Create a new SWF from scratch."},
418     {0,0,0,0}
419     // save is a member function
420 };
421 PyMethodDef* swf_getMethods()
422 {
423     SWFClass.ob_type = &PyType_Type;
424     return SWFMethods;
425 }
426
427 // =============================================================================
428
429 #include "primitives.h"
430 #include "action.h"
431 #include "tag.h"
432 #include "taglist.h"
433
434 static PyObject* module_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
435 {
436     int _verbose = 0;
437     static char *kwlist[] = {"verbosity", NULL};
438     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &_verbose))
439         return NULL;
440     setVerbosity(_verbose);
441
442     return Py_BuildValue("s", 0);
443 }
444
445 static PyMethodDef LoggingMethods[] = 
446 {
447     /* Module functions */
448     {"verbose", (PyCFunction)module_verbose, METH_KEYWORDS, "Set the module verbosity"},
449     {0,0,0,0}
450 };
451
452 void initSWF(void)
453 {
454     PyObject*module;
455     PyMethodDef* primitive_methods = primitive_getMethods();
456     PyMethodDef* tag_methods = tags_getMethods();
457     PyMethodDef* action_methods = action_getMethods();
458     PyMethodDef* swf_methods = swf_getMethods();
459
460     PyMethodDef* all_methods = 0;
461     all_methods = addMethods(all_methods, primitive_methods);
462     all_methods = addMethods(all_methods, tag_methods);
463     all_methods = addMethods(all_methods, action_methods);
464     all_methods = addMethods(all_methods, swf_methods);
465
466     all_methods = addMethods(all_methods, LoggingMethods);
467
468     module = Py_InitModule("SWF", all_methods);
469
470     /* Python doesn't copy the PyMethodDef struct, so we need
471        to keep it around */
472     // free(all_methods) 
473 }