fixed a nasty floating point bug
[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         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_WriteSWF(fi, swf)<0) {
215         close(fi);
216         PyErr_SetString(PyExc_Exception, setError("WriteSWC() failed."));
217         return 0;
218     }
219     close(fi);
220
221     swf_FreeTags(swf);
222     /*{ TAG * t = swf->firstTag;
223       while (t)
224       { 
225         mylog("tag: %08x\n",t);
226         mylog("  id: %d (%s)\n", t->id, swf_TagGetName(t));
227         mylog("  data: %08x (%d bytes)\n", t->data, t->len);
228         mylog("  next: %08x\n", t->next);
229         TAG * tnew = t->next;
230         mylog("->free data\n");
231         if (t->data) free(t->data);
232         mylog("->free tag\n");
233         free(t);
234         t = tnew;
235       }
236     }*/
237     swf->firstTag = 0;
238     
239     mylog(" %08x(%d) f_save filename=%s done\n", (int)self, self->ob_refcnt, filename);
240     
241     return PY_NONE;
242 }
243 //----------------------------------------------------------------------------
244 static PyObject * swf_writeCGI(PyObject* self, PyObject* args)
245 {
246     SWFObject*swf = (SWFObject*)self;
247     if(!self || !PyArg_ParseTuple(args,"")) 
248         return NULL;
249     swf->swf.firstTag = taglist_getTAGs(swf->taglist);
250     if(!swf->swf.firstTag)
251         return NULL;
252     swf_WriteCGI(&swf->swf);
253     swf_FreeTags(&swf->swf);
254     swf->swf.firstTag = 0;
255     return PY_NONE;
256 }
257 //----------------------------------------------------------------------------
258
259 //TODO: void swf_Relocate(SWF*swf, char*bitmap); // bitmap is 65536 bytes, bitmap[a]==0 means id a is free
260
261 static PyMethodDef swf_functions[] =
262 {{"save", (PyCFunction)swf_save, METH_KEYWORDS, "Save SWF to disk"},
263  {"writeCGI", (PyCFunction)swf_writeCGI, METH_VARARGS, "print SWF as CGI to stdout"},
264  {NULL, NULL, 0, NULL}
265 };
266  
267 //----------------------------------------------------------------------------
268 static void swf_dealloc(PyObject* self)
269 {
270     mylog("-%08x(%d) swf_dealloc\n", (int)self, self->ob_refcnt);
271     SWFObject*swfo;
272     SWF*swf;
273     swfo = (SWFObject*)self;
274     swf = &swfo->swf;
275     if(swfo->filename) {
276         free(swfo->filename);
277         swfo->filename = 0;
278     }
279     Py_DECREF(swfo->taglist);
280     swfo->taglist = 0;
281     PyObject_Del(self);
282 }
283 //----------------------------------------------------------------------------
284 static int swf_print(PyObject * self, FILE *fi, int flags) //flags&Py_PRINT_RAW
285 {
286     mylog(" %08x(%d) print \n", (int)self, self->ob_refcnt);
287     SWFObject*swf = (SWFObject*)self;
288     swf_DumpHeader(fi, &swf->swf);
289     //void swf_DumpSWF(FILE * f,SWF*swf);
290     return 0;
291 }
292 //----------------------------------------------------------------------------
293 static PyObject* swf_getattr(PyObject * self, char* a)
294 {
295     SWFObject*swf = (SWFObject*)self;
296     PyObject* ret = 0;
297
298     if(!strcmp(a, "fps")) {
299         double fps = swf->swf.frameRate/256.0;
300         mylog(" %08x(%d) swf_getattr %s = %f\n", (int)self, self->ob_refcnt, a, fps);
301         return Py_BuildValue("d", fps);
302     } else if(!strcmp(a, "version")) {
303         int version = swf->swf.fileVersion;;
304         mylog(" %08x(%d) swf_getattr %s = %d\n", (int)self, self->ob_refcnt, a, version);
305         return Py_BuildValue("i", version);
306     } else if(!strcmp(a, "name")) {
307         char*filename = swf->filename;
308         mylog(" %08x(%d) swf_getattr %s = %s\n", (int)self, self->ob_refcnt, a, filename);
309         return Py_BuildValue("s", filename);
310     } else if(!strcmp(a, "bbox")) {
311         return f_BBox2(swf->swf.movieSize);
312     } else if(!strcmp(a, "tags")) {
313         PyObject*ret =  (PyObject*)(swf->taglist);
314         Py_INCREF(ret);
315         mylog(" %08x(%d) swf_getattr %s = %08x(%d)\n", (int)self, self->ob_refcnt, a, ret, ret->ob_refcnt);
316         return ret;
317     } else if(!strcmp(a, "filesize")) {
318         int s = swf->swf.fileSize;
319         return Py_BuildValue("i", s);
320     } else if(!strcmp(a, "width")) {
321         int w = (swf->swf.movieSize.xmax - swf->swf.movieSize.xmin) / 20;
322         return Py_BuildValue("i", w);
323     } else if(!strcmp(a, "height")) {
324         int h =  (swf->swf.movieSize.ymax - swf->swf.movieSize.ymin) / 20;
325         return Py_BuildValue("i", h);
326     } else if(!strcmp(a, "framecount")) {
327         int f = swf->swf.frameCount;
328         return Py_BuildValue("i", f);
329     }
330
331     ret = Py_FindMethod(swf_functions, self, a);
332     mylog(" %08x(%d) swf_getattr %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
333     return ret;
334 }
335 //----------------------------------------------------------------------------
336 static int swf_setattr(PyObject * self, char* a, PyObject * o)
337 {
338     SWFObject*swf = (SWFObject*)self;
339     if(!strcmp(a, "fps")) {
340         double fps;
341         if (!PyArg_Parse(o, "d", &fps)) 
342             goto err;
343         swf->swf.frameRate = (int)(fps*0x100);
344         mylog(" %08x(%d) swf_setattr %s = %f\n", (int)self, self->ob_refcnt, a, fps);
345         return 0;
346     } else if(!strcmp(a, "version")) {
347         int version;
348         if (!PyArg_Parse(o, "i", &version)) 
349             goto err;
350         swf->swf.fileVersion = version;
351         mylog(" %08x(%d) swf_setattr %s = %d\n", (int)self, self->ob_refcnt, a, version);
352         return 0;
353     } else if(!strcmp(a, "name")) {
354         char*filename;
355         if (!PyArg_Parse(o, "s", &filename)) 
356             goto err;
357         if(swf->filename) {
358             free(swf->filename);swf->filename=0;
359         }
360         swf->filename = strdup(filename);
361         mylog(" %08x(%d) swf_setattr %s = %s\n", (int)self, self->ob_refcnt, a, filename);
362         return 0;
363     } else if(!strcmp(a, "bbox")) {
364         PyObject *obbox = o;
365         if (!PY_CHECK_TYPE(obbox, &BBoxClass)) {
366             obbox = f_BBox(0, o, 0);
367             if(!obbox)
368                 return 1;
369         }
370         SRECT bbox = bbox_getSRECT(obbox);
371
372         swf->swf.movieSize = bbox;
373         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);
374         return 0;
375     } else if(!strcmp(a, "tags")) {
376         PyObject* taglist;
377         taglist = o;
378         PY_ASSERT_TYPE(taglist,&TagListClass);
379         Py_DECREF(swf->taglist);
380         swf->taglist = taglist;
381         Py_INCREF(swf->taglist);
382         mylog(" %08x(%d) swf_setattr %s = %08x\n", (int)self, self->ob_refcnt, a, swf->taglist);
383         return 0;
384     }
385 err:
386     mylog(" %08x(%d) swf_setattr %s = ? (%08x)\n", (int)self, self->ob_refcnt, a, o);
387     return 1;
388 }
389
390 //----------------------------------------------------------------------------
391 static PyTypeObject SWFClass = 
392 {
393     PyObject_HEAD_INIT(NULL)
394     0,
395     tp_name: "SWF",
396     tp_basicsize: sizeof(SWFObject),
397     tp_itemsize: 0,
398     tp_dealloc: swf_dealloc,
399     tp_print: swf_print,
400     tp_getattr: swf_getattr,
401     tp_setattr: swf_setattr,
402 };
403 //----------------------------------------------------------------------------
404
405 static PyMethodDef SWFMethods[] = 
406 {
407     /* SWF creation*/
408     {"load", (PyCFunction)f_load, METH_KEYWORDS, "Load a SWF from disc."},
409     {"create", (PyCFunction)f_create, METH_KEYWORDS, "Create a new SWF from scratch."},
410     {0,0,0,0}
411     // save is a member function
412 };
413 PyMethodDef* swf_getMethods()
414 {
415     SWFClass.ob_type = &PyType_Type;
416     return SWFMethods;
417 }
418
419 // =============================================================================
420
421 #include "primitives.h"
422 #include "action.h"
423 #include "tag.h"
424 #include "taglist.h"
425
426 static PyObject* module_verbose(PyObject* self, PyObject* args, PyObject* kwargs)
427 {
428     int _verbose = 0;
429     static char *kwlist[] = {"verbosity", NULL};
430     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", kwlist, &_verbose))
431         return NULL;
432     setVerbosity(_verbose);
433
434     return Py_BuildValue("s", 0);
435 }
436
437 static PyMethodDef LoggingMethods[] = 
438 {
439     /* Module functions */
440     {"verbose", (PyCFunction)module_verbose, METH_KEYWORDS, "Set the module verbosity"},
441     {0,0,0,0}
442 };
443
444 void initSWF(void)
445 {
446     PyObject*module;
447     PyMethodDef* primitive_methods = primitive_getMethods();
448     PyMethodDef* tag_methods = tags_getMethods();
449     PyMethodDef* action_methods = action_getMethods();
450     PyMethodDef* swf_methods = swf_getMethods();
451
452     PyMethodDef* all_methods = 0;
453     all_methods = addMethods(all_methods, primitive_methods);
454     all_methods = addMethods(all_methods, tag_methods);
455     all_methods = addMethods(all_methods, action_methods);
456     all_methods = addMethods(all_methods, swf_methods);
457
458     all_methods = addMethods(all_methods, LoggingMethods);
459
460     module = Py_InitModule("SWF", all_methods);
461
462     /* Python doesn't copy the PyMethodDef struct, so we need
463        to keep it around */
464     // free(all_methods) 
465 }