some fixes, added "verbose" constant.
[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 "primitives.h"
29 #include "action.h"
30
31 /*
32 TODO:
33     1) taglist is rfxswflib's linked list. It should maybe implemented as Python
34        list, which would, however, mean that we would have to convert the list
35        back and forth for the following functions:
36         load, save, writeCGI, unfoldAll, foldAll, optimizeOrder
37     2) taglist should have an ID handler. Every time a tag is inserted, it's ID
38        is stored in a lookup list.
39     3) 
40 */
41
42 //-------------------------- Types -------------------------------------------
43
44 staticforward PyTypeObject SWFClass;
45 staticforward PyTypeObject TagListClass;
46 staticforward PyTypeObject TagClass;
47
48 /* Tags, Objects */
49
50 typedef struct {
51     PyObject_HEAD
52     TAG*tag;
53     /* ST_DEFINEFONT*/
54     SWFFONT* font;
55     /* ST_PLACEOBJECT, ST_PLACEOBJECT2*/
56     SWFPLACEOBJECT* placeobject;
57     PyObject* character;
58 } TagObject;
59
60 typedef struct {
61     PyObject_HEAD
62     TAG*firstTag;
63     TAG*searchTag;
64     TAG*lastTag;
65     PyDictObject* char2id;
66     PyDictObject* id2char;
67     U16 currentID;
68 } TagListObject;
69
70 typedef struct {
71     PyObject_HEAD
72     SWF swf; //swf.firstTag ist not used
73     TagListObject*taglist;
74     char*filename;
75 } SWFObject;
76
77 //----------------------------------------------------------------------------
78 static PyObject* f_create(PyObject* self, PyObject* args, PyObject* kwargs)
79 {
80     static char *kwlist[] = {"version", "fps", "bbox", "name", NULL};
81     SWFObject* swf;
82     int version = 6;
83     double framerate = 25;
84     PyObject * obbox = 0;
85     SRECT bbox = {0,0,0,0};
86     char* filename = 0;
87
88     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|idOs", 
89                 kwlist, &version, &framerate, 
90                 &obbox, filename))
91         return NULL;
92
93     if (!PyArg_Parse(obbox, "(iiii)", &bbox.xmin, &bbox.ymin, &bbox.xmax, &bbox.ymax))
94         return NULL;
95
96     swf = PyObject_New(SWFObject, &SWFClass);
97     memset(&swf->swf, 0, sizeof(SWF));
98     if(filename)
99         swf->filename = strdup(filename);
100     else
101         swf->filename = 0;
102
103     swf->swf.fileVersion = version;
104     swf->swf.frameRate = (int)(framerate*0x100);
105     swf->swf.movieSize = bbox;
106     swf->taglist = PyObject_New(TagListObject, &TagListClass);
107     swf->taglist->firstTag = 0;
108     swf->taglist->searchTag = 0;
109     swf->taglist->lastTag = 0;
110     swf->taglist->currentID = 1;
111     swf->taglist->char2id = (PyDictObject*)PyDict_New();
112     swf->taglist->id2char = (PyDictObject*)PyDict_New();
113
114     if(swf->swf.fileVersion>=6)
115         swf->swf.compressed = 1;
116
117     mylog("create %08x -> %08x\n", (int)self, (int)swf);
118     return (PyObject*)swf;
119 }
120 //----------------------------------------------------------------------------
121 static PyObject* f_load(PyObject* self, PyObject* args)
122 {
123     char* filename;
124     SWFObject* swf;
125     int fi;
126
127     if (!PyArg_ParseTuple(args,"s:load", &filename)) 
128         return NULL;
129
130     swf = PyObject_New(SWFObject, &SWFClass);
131     memset(&swf->swf, 0, sizeof(SWF));
132     swf->filename = strdup(filename);
133     swf->taglist = PyObject_New(TagListObject, &TagListClass);
134
135     if(!filename) {
136         PyErr_SetString(PyExc_Exception, setError("Couldn't open file %s", filename));
137         return 0;
138     }
139     fi = open(filename,O_RDONLY|O_BINARY);
140     if (fi<0) { 
141         PyErr_SetString(PyExc_Exception, setError("Couldn't open file %s", filename));
142         return 0;
143     }
144     if(swf_ReadSWF(fi,&swf->swf)<0) { 
145         close(fi);
146         PyErr_SetString(PyExc_Exception, setError("%s is not a valid SWF file or contains errors",filename));
147         return 0;
148     }
149     close(fi);
150     
151     swf->taglist->firstTag = swf->swf.firstTag;
152     swf->taglist->searchTag = swf->swf.firstTag;
153     swf->taglist->lastTag = swf->swf.firstTag;
154     while(swf->taglist->lastTag->next)
155         swf->taglist->lastTag = swf->taglist->lastTag->next;
156     swf->taglist->currentID = 1;
157     swf->taglist->char2id = (PyDictObject*)PyDict_New();
158     swf->taglist->id2char = (PyDictObject*)PyDict_New();
159     swf->swf.firstTag = 0;
160
161     mylog("load %08x -> %08x\n", (int)self, (int)swf);
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     // keyword arg compress (=1) forces compression
186     if(compress)
187         swf->compressed = 1;
188     
189     swf->firstTag = swfo->taglist->firstTag;
190
191     // fix the file, in case it is empty or not terminated properly
192     {
193         TAG*tag = swf->firstTag;
194         if(!tag)
195             tag = swf->firstTag = swf_InsertTag(0,ST_END);
196         while(tag && tag->next)
197             tag = tag->next;
198         if(tag->id != ST_END) {
199             tag = swf_InsertTag(tag,ST_END);
200         }
201     }
202
203     fi = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644);
204     if(fi<0) {
205         PyErr_SetString(PyExc_Exception, setError("couldn't create output file %s", filename));
206         return 0;
207     }
208     if(swf->compressed) {
209             if(swf_WriteSWC(fi, swf)<0) {
210                 close(fi);
211                 PyErr_SetString(PyExc_Exception, setError("WriteSWC() failed."));
212                 return 0;
213             }
214     } else {
215             if(swf_WriteSWF(fi, swf)<0) {
216                 close(fi);
217                 PyErr_SetString(PyExc_Exception, setError("WriteSWC() failed."));
218                 return 0;
219             }
220     }
221     close(fi);
222     
223     swf->firstTag = 0;
224     
225     return PY_NONE;
226 }
227 //----------------------------------------------------------------------------
228 static PyObject * swf_writeCGI(PyObject* self, PyObject* args)
229 {
230     SWFObject*swf = (SWFObject*)self;
231     if(!self || !PyArg_ParseTuple(args,"")) 
232         return NULL;
233     swf->swf.firstTag = swf->taglist->firstTag;
234     swf_WriteCGI(&swf->swf);
235     swf->swf.firstTag = 0;
236     return PY_NONE;
237 }
238 //----------------------------------------------------------------------------
239 static PyObject * taglist_foldAll(PyObject* self, PyObject* args)
240 {
241     SWF swf;
242     TagListObject*taglist = (TagListObject*)self;
243     if(!self || !PyArg_ParseTuple(args,"")) 
244         return NULL;
245     swf.firstTag = taglist->firstTag;
246     swf_FoldAll(&swf);
247     taglist->firstTag = swf.firstTag;
248     taglist->lastTag = 0; // FIXME
249     taglist->searchTag = 0;
250     return PY_NONE;
251 }
252 //----------------------------------------------------------------------------
253 static PyObject * taglist_unfoldAll(PyObject* self, PyObject* args)
254 {
255     SWF swf;
256     TagListObject*taglist = (TagListObject*)self;
257     if(!self || !PyArg_ParseTuple(args,"")) 
258         return NULL;
259     swf.firstTag = taglist->firstTag;
260     swf_UnFoldAll(&swf);
261     taglist->firstTag = swf.firstTag;
262     taglist->lastTag = 0; // FIXME
263     taglist->searchTag = 0;
264     return PY_NONE;
265 }
266 //----------------------------------------------------------------------------
267 static PyObject * taglist_optimizeOrder(PyObject* self, PyObject* args)
268 {
269     SWF swf;
270     TagListObject*taglist = (TagListObject*)self;
271     if(!self || !PyArg_ParseTuple(args,"")) 
272         return NULL;
273     swf.firstTag = taglist->firstTag;
274     swf_UnFoldAll(&swf);
275     taglist->firstTag = swf.firstTag;
276     taglist->lastTag = 0; // FIXME
277     taglist->searchTag = 0;
278     return PY_NONE;
279 }
280 //----------------------------------------------------------------------------
281
282 //TODO: void swf_Relocate(SWF*swf, char*bitmap); // bitmap is 65536 bytes, bitmap[a]==0 means id a is free
283
284 static PyMethodDef swf_functions[] =
285 {{"save", (PyCFunction)swf_save, METH_KEYWORDS, "Save SWF to disk"},
286  {"writeCGI", (PyCFunction)swf_writeCGI, METH_VARARGS, "print SWF as CGI to stdout"},
287  {NULL, NULL, 0, NULL}
288 };
289  
290 //----------------------------------------------------------------------------
291 static void swf_dealloc(PyObject* self)
292 {
293     SWFObject*swfo;
294     SWF*swf;
295     swfo = (SWFObject*)self;
296     swf = &swfo->swf;
297     mylog("swf_dealloc %08x(%d)\n", (int)self, self->ob_refcnt);
298     if(swfo->filename) {
299         free(swfo->filename);
300         swfo->filename = 0;
301     }
302     Py_DECREF(swfo->taglist);
303     swfo->taglist = 0;
304     PyObject_Del(self);
305 }
306 //----------------------------------------------------------------------------
307 static void taglist_dealloc(PyObject* self)
308 {
309     TagListObject*taglist = (TagListObject*)self;
310     SWF swf;
311     mylog("taglist_dealloc %08x(%d)\n", (int)self, self->ob_refcnt);
312     swf.firstTag = taglist->firstTag;
313     swf_FreeTags(&swf);
314     taglist->firstTag = 0;
315     taglist->lastTag = 0;
316     taglist->searchTag = 0;
317     PyObject_Del(self);
318 }
319 //----------------------------------------------------------------------------
320 static int swf_print(PyObject * self, FILE *fi, int flags) //flags&Py_PRINT_RAW
321 {
322     SWFObject*swf = (SWFObject*)self;
323     swf_DumpHeader(fi, &swf->swf);
324     //void swf_DumpSWF(FILE * f,SWF*swf);
325     mylog("print %08x(%d)\n", (int)self, self->ob_refcnt);
326     return 0;
327 }
328 //----------------------------------------------------------------------------
329 static PyObject* swf_getattr(PyObject * self, char* a)
330 {
331     SWFObject*swf = (SWFObject*)self;
332     PyObject* ret;
333
334     if(!strcmp(a, "fps")) {
335         double fps = swf->swf.frameRate/256.0;
336         mylog("swf_getattr %08x(%d) %s = %f\n", (int)self, self->ob_refcnt, a, fps);
337         return Py_BuildValue("d", fps);
338     } else if(!strcmp(a, "version")) {
339         int version = swf->swf.fileVersion;;
340         mylog("swf_getattr %08x(%d) %s = %d\n", (int)self, self->ob_refcnt, a, version);
341         return Py_BuildValue("i", version);
342     } else if(!strcmp(a, "name")) {
343         char*filename = swf->filename;
344         mylog("swf_getattr %08x(%d) %s = %s\n", (int)self, self->ob_refcnt, a, filename);
345         return Py_BuildValue("s", filename);
346     } else if(!strcmp(a, "bbox")) {
347         int xmin,ymin,xmax,ymax;
348         xmin = swf->swf.movieSize.xmin;
349         ymin = swf->swf.movieSize.ymin;
350         xmax = swf->swf.movieSize.xmax;
351         ymax = swf->swf.movieSize.ymax;
352         mylog("swf_getattr %08x(%d) %s = (%d,%d,%d,%d)\n", (int)self, self->ob_refcnt, a, xmin,ymin,xmax,ymax);
353         return Py_BuildValue("(iiii)", xmin, ymin, xmax, ymax); 
354     } else if(!strcmp(a, "tags")) {
355         PyObject*ret =  (PyObject*)(swf->taglist);
356         Py_INCREF(ret);
357         mylog("swf_getattr %08x(%d) %s = %08x(%d)\n", (int)self, self->ob_refcnt, a, ret, ret->ob_refcnt);
358         return ret;
359     }
360
361     ret = Py_FindMethod(swf_functions, self, a);
362     mylog("swf_getattr %08x(%d) %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
363     return ret;
364 }
365 //----------------------------------------------------------------------------
366 static int swf_setattr(PyObject * self, char* a, PyObject * o)
367 {
368     SWFObject*swf = (SWFObject*)self;
369     if(!strcmp(a, "fps")) {
370         double fps;
371         if (!PyArg_Parse(o, "d", &fps)) 
372             goto err;
373         swf->swf.frameRate = (int)(fps*0x100);
374         mylog("swf_setattr %08x(%d) %s = %f\n", (int)self, self->ob_refcnt, a, fps);
375         return 0;
376     } else if(!strcmp(a, "version")) {
377         int version;
378         if (!PyArg_Parse(o, "i", &version)) 
379             goto err;
380         swf->swf.fileVersion = version;
381         mylog("swf_setattr %08x(%d) %s = %d\n", (int)self, self->ob_refcnt, a, version);
382         return 0;
383     } else if(!strcmp(a, "name")) {
384         char*filename;
385         if (!PyArg_Parse(o, "s", &filename)) 
386             goto err;
387         if(swf->filename) {
388             free(swf->filename);swf->filename=0;
389         }
390         swf->filename = strdup(filename);
391         mylog("swf_setattr %08x(%d) %s = %s\n", (int)self, self->ob_refcnt, a, filename);
392         return 0;
393     } else if(!strcmp(a, "bbox")) {
394         int xmin=0,ymin=0,xmax=0,ymax=0;
395         if (!PyArg_Parse(o, "(iiii)", &xmin, &ymin, &xmax, &ymax)) 
396             goto err;
397
398         swf->swf.movieSize.xmin = xmin;
399         swf->swf.movieSize.ymin = ymin;
400         swf->swf.movieSize.xmax = xmax;
401         swf->swf.movieSize.ymax = ymax;
402         mylog("swf_setattr %08x(%d) %s = (%d,%d,%d,%d)\n", (int)self, self->ob_refcnt, a, xmin,ymin,xmax,ymax);
403         return 0;
404     } else if(!strcmp(a, "tags")) {
405         TagListObject* taglist;
406         /*if (!PyArg_Parse(o, "O!", &TagListClass, &taglist));
407             goto err;*/
408         taglist = (TagListObject*)o;
409         Py_DECREF(swf->taglist);
410         swf->taglist = taglist;
411         Py_INCREF(swf->taglist);
412         mylog("swf_setattr %08x(%d) %s = %08x\n", (int)self, self->ob_refcnt, a, swf->taglist);
413         return 0;
414     }
415 err:
416     mylog("swf_setattr %08x(%d) %s = ? (%08x)\n", (int)self, self->ob_refcnt, a, o);
417     return 1;
418 }
419
420 //----------------------------------------------------------------------------
421 static PyMethodDef taglist_functions[] =
422 {{"foldAll", taglist_foldAll, METH_VARARGS, "fold all sprites (movieclips) in the list"},
423  {"unfoldAll", taglist_unfoldAll, METH_VARARGS, "unfold (expand) all sprites (movieclips) in the list"},
424  {"optimizeOrder", taglist_optimizeOrder, METH_VARARGS, "Reorder the Tag structure"},
425  {NULL, NULL, 0, NULL}
426 };
427
428 static PyObject* taglist_getattr(PyObject * self, char* a)
429 {
430     PyObject* ret = Py_FindMethod(taglist_functions, self, a);
431     mylog("taglist_getattr %08x(%d) %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
432     return ret;
433 }
434 //----------------------------------------------------------------------------
435 static int taglist_length(PyObject * self)
436 {
437     TagListObject*tags = (TagListObject*)self;
438     TAG*tag;
439     int l = 0;
440     mylog("taglist_length %08x(%d)", (int)self, self->ob_refcnt);
441     tag = tags->firstTag;
442     while(tag) {
443         l++;
444         tag = tag->next;
445     }
446     return l;
447 }
448 //----------------------------------------------------------------------------
449 static PyObject * taglist_concat(PyObject * self, PyObject* list)
450 {
451     TagObject*tag;
452     TagListObject*taglist = (TagListObject*)self;
453     mylog("taglist_concat %08x(%d) %08x", (int)self, self->ob_refcnt, list);
454         
455     if (PyArg_Parse(list, "O!", &TagClass, &tag)) {
456         /* copy tag, so we don't have to do INCREF(tag) (and don't
457            get problems if the tag is appended to more than one
458            taglist) */
459         /* TODO: handle IDs */
460         mylog("taglist_concat: Tag", (int)self, self->ob_refcnt);
461         taglist->lastTag = swf_InsertTag(taglist->lastTag, tag->tag->id);
462         swf_SetBlock(taglist->lastTag, tag->tag->data, tag->tag->len);
463         if(!taglist->firstTag) {
464             taglist->firstTag = taglist->searchTag = taglist->lastTag;
465         }
466         if(swf_isDefiningTag(tag->tag)) {
467             PyObject*id = PyLong_FromLong(taglist->currentID);
468             PyDict_SetItem((PyObject*)(taglist->char2id), list, id);
469             Py_INCREF(id);
470             PyDict_SetItem((PyObject*)(taglist->id2char), id, list);
471             Py_INCREF(id);
472         }
473         Py_INCREF(self);
474         return self;
475     }
476     PyErr_Clear();
477     if (PyList_Check(list)) {
478         int l = PyList_Size(list);
479         int t;
480         mylog("taglist_concat: PythonList", (int)self, self->ob_refcnt);
481         for(t=0;t<l;t++) {
482             PyObject*item = PyList_GetItem(list, t);
483             self = taglist_concat(self, item);
484             if(!self)
485                 return 0;
486         }
487         Py_INCREF(self);
488         return self;
489     }
490     PyErr_SetString(PyExc_Exception, setError("taglist concatenation only works with tags and lists (%08x).", list));
491     return 0;
492 }
493 //----------------------------------------------------------------------------
494 staticforward TagObject* TagObject_New();
495 static PyObject * taglist_item(PyObject * self, int index)
496 {
497     TagListObject*taglist = (TagListObject*)self;
498     TAG*tag;
499     TagObject*tagobject;
500     int i = 0;
501     mylog("taglist_item %08x(%d) [%d]", (int)self, self->ob_refcnt, index);
502
503     if(index<0) {
504         PyErr_SetString(PyExc_Exception, setError("Negative Indices not supported."));
505         return NULL;
506     }
507
508     tag = taglist->firstTag;
509     while(tag && i<index) {
510         tag = tag->next;
511         i++;
512     }
513     if(!tag || i != index) {
514         if(index> i+10) {
515             PyErr_SetString(PyExc_Exception, setError("No Tag at position %d", index));
516             return NULL;
517         }
518
519         mylog("taglist_item %08x(%d)->IndexError (%d)", (int)self, self->ob_refcnt, index);
520
521         Py_INCREF(PyExc_IndexError);
522         PyErr_SetObject(PyExc_IndexError, Py_None);
523         return NULL;
524     }
525
526     tagobject = TagObject_New();
527     tagobject->tag = tag;
528
529     return (PyObject*)tagobject;
530 }
531 //----------------------------------------------------------------------------
532 static void tag_dealloc(PyObject * self)
533 {
534     TagObject*tag = (TagObject*)self;
535     mylog("tag_dealoc %08x(%d)\n", (int)self, self->ob_refcnt);
536     if(tag->placeobject) {
537         swf_PlaceObjectFree(tag->placeobject);
538         tag->placeobject = 0;
539     }
540     if(tag->font) {
541         swf_FontFree(tag->font);
542         tag->font = 0;
543     }
544     if(tag->character) {
545         Py_DECREF(tag->character);
546         tag->character = 0;
547     }
548     PyObject_Del(self);
549 }
550 //----------------------------------------------------------------------------
551 static PyObject* tag_setU8(PyObject * self, PyObject*other)
552 {
553     return NULL;
554 }
555 //----------------------------------------------------------------------------
556 static PyObject* tag_setbackgroundcolor_getrgb(PyObject * self, PyObject*other)
557 {
558     TagObject*tag = (TagObject*)self;
559     int r,g,b;
560     r = tag->tag->data[0];
561     g = tag->tag->data[1];
562     b = tag->tag->data[2];
563     return Py_BuildValue("(iii)", r,g,b);
564 }
565 //----------------------------------------------------------------------------
566
567 static struct tagfunctions {
568     int id;
569     PyMethodDef f[8];
570 } tagfunctions[] =
571 {
572  { 
573    ST_SETBACKGROUNDCOLOR, 
574    {{"getRGB", tag_setbackgroundcolor_getrgb, METH_VARARGS, "get's the color set by this tag"},
575     {NULL, NULL, 0, NULL}
576    }
577  }
578 };
579 static PyMethodDef common_tagfunctions[] =
580 {{"setU8", tag_setU8, METH_VARARGS, "sets a byte to the tag data"},
581  {NULL, NULL, 0, NULL}
582 };
583
584 static PyObject* tag_getattr(PyObject * self, char* a)
585 {
586     TagObject*tag = (TagObject*)self;
587     PyObject* ret = NULL;
588     int id =  tag->tag->id;
589     int t;
590    
591     /* search for a tag specific function */
592     for(t=0;t<sizeof(tagfunctions)/sizeof(tagfunctions[0]);t++)
593     {
594         if(id==tagfunctions[t].id) {
595             mylog("tag_getattr: id %d found\n", id);
596             ret = Py_FindMethod(tagfunctions[t].f, self, a);
597             if(!ret) return ret;
598             ret = FindMethodMore(ret, common_tagfunctions, self, a);
599             mylog("tag_getattr %08x(%d) %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
600             return ret;
601         }
602     }
603    
604     ret = Py_FindMethod(common_tagfunctions, self, a);
605
606     mylog("tag_getattr %08x(%d) %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
607     return ret;
608 }
609 //----------------------------------------------------------------------------
610 //                     Tag Contructors
611 //----------------------------------------------------------------------------
612 static TagObject* TagObject_New()
613 {
614     TagObject*tag = PyObject_New(TagObject, &TagClass);
615     tag->font = 0;
616     tag->character = 0;
617     tag->placeobject = 0;
618     tag->tag = 0;
619     return tag;
620 }
621
622 static PyObject* f_SetBackgroundColor(PyObject* self, PyObject* args, PyObject* kwargs)
623 {
624     static char *kwlist[] = {"color", NULL};
625     int r=0,g=0,b=0;
626     TagObject*tag;
627     ColorObject*color;
628
629     /* 1st try- copy constructor */
630     if(!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &ColorClass, &color)) {
631         /* 2nd try- color's contructor */
632         color = (ColorObject*)f_Color(NULL, args, kwargs);
633     }
634     if(!color)
635         return NULL;
636
637     tag = TagObject_New();
638     tag->tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
639     swf_SetU8(tag->tag, color->rgba.r);
640     swf_SetU8(tag->tag, color->rgba.g);
641     swf_SetU8(tag->tag, color->rgba.b);
642     mylog("SetBackgroundColor(%02x,%02x,%02x) %08x -> %08x\n", color->rgba.r, color->rgba.g, color->rgba.b, (int)self, tag);
643     return (PyObject*)tag;
644 }
645 //----------------------------------------------------------------------------
646 static PyObject* f_DefineFont(PyObject* self, PyObject* args, PyObject* kwargs)
647 {
648     static char *kwlist[] = {"filename", NULL};
649     char*filename = 0;
650     TagObject*tag;
651     SWFFONT* font;
652
653     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &filename))
654         return NULL;
655
656     font = swf_ReadFont(filename);
657     mylog("font=%08x",font);
658     if(!font) {
659         PyErr_SetString(PyExc_Exception, setError("Could not load %s", filename));
660         return NULL;
661     }
662
663     tag = TagObject_New();
664     tag->font = font;
665     tag->tag = swf_InsertTag(0, ST_DEFINEFONT2);
666     tag->font->id = 0xabcd; // swf_SetU16(id);
667     swf_FontSetDefine2(tag->tag, tag->font); // TODO: should this be done later, in taglist?
668     mylog("DefineFont %08x -> %08x\n", (int)self, (int)tag);
669     return (PyObject*)tag;
670 }
671 //----------------------------------------------------------------------------
672 static PyObject* f_Protect(PyObject* self, PyObject* args, PyObject* kwargs)
673 {
674     static char *kwlist[] = {"password", NULL};
675     char*password = 0;
676     TagObject*tag;
677
678     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &password))
679         return NULL;
680
681     tag = TagObject_New();
682     tag->tag = swf_InsertTag(0, ST_PROTECT);
683     if(password) {
684         swf_SetPassword(tag->tag, password);
685     }
686     mylog("f_Protect %08x -> %08x\n", (int)self, (int)tag);
687     return (PyObject*)tag;
688 }
689 //----------------------------------------------------------------------------
690 static PyObject* f_DefineText(PyObject* self, PyObject* args, PyObject* kwargs)
691 {
692     static char *kwlist[] = {"font", "text", "size", "color", NULL};
693     TagObject*tag;
694     char*text = 0;
695     int textlen = 0;
696     PyObject*unicode16;
697     PyObject*unicode8;
698     int size = 0;
699     RGBA rgba = {0,0,0,255};
700     ColorObject*color = 0;
701     TagObject*font = 0;
702     SRECT r;
703     
704     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!u#i|O!", kwlist, &TagClass, &font, &text, &textlen, &size, &ColorClass, &color))
705         return NULL;
706     
707     unicode16 = PyUnicode_DecodeUTF16(text, textlen*2, NULL, NULL);
708     unicode8 = PyUnicode_AsUTF8String(unicode16);
709     text = PyString_AS_STRING(unicode8);
710
711     if(color)
712         rgba = color->rgba;
713
714     mylog("DefineText: text = %s", text);
715     
716     tag = TagObject_New();
717     tag ->tag= swf_InsertTag(0, ST_DEFINETEXT2);
718     swf_SetU16(tag->tag, /*ID*/0);
719     r = swf_SetDefineText(tag->tag, font->font, &rgba, text, size);
720     mylog("DefineText %08x -> %08x\n", (int)self, (int)tag);
721     return (PyObject*)tag;
722 }
723 //----------------------------------------------------------------------------
724 static PyObject* f_PlaceObject(PyObject* self, PyObject* args, PyObject* kwargs)
725 {
726     static char *kwlist[] = {"character", "depth", "matrix", "colortransform", "ratio", "name", "clipdepth", "action", NULL};
727     TagObject*tag;
728     
729     TagObject*character = 0;
730     int depth;
731     int clipdepth = 0;
732     MatrixObject*matrix = 0;
733     CXFormObject*cxform = 0;
734     int ratio = 0;
735     ActionObject*action = 0;
736     char* name = 0;
737     SWFPLACEOBJECT* po;
738     po = malloc(sizeof(SWFPLACEOBJECT));
739     memset(po, 0, sizeof(SWFPLACEOBJECT));
740
741     swf_GetPlaceObject(0, po);
742
743     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!i|O!O!isiO!", kwlist, 
744                 &TagClass, &character, 
745                 &depth, 
746                 &MatrixClass, &matrix, 
747                 &CXFormClass, &cxform,
748                 &ratio,
749                 &name,
750                 &clipdepth,
751                 &action
752                 ))
753         return NULL;
754     po->depth = depth;
755     po->id = /*ID*/ 0;
756     po->clipdepth = clipdepth;
757     po->ratio = ratio;
758     po->name = name;
759     if(clipdepth) po->matrix = matrix->matrix;
760     if(cxform) po->cxform = cxform->cxform;
761     if(action) po->actions = action->action;
762
763     tag = TagObject_New();
764     tag->placeobject = po;
765     Py_INCREF(character);
766     tag->character = (PyObject*)character;
767     tag->tag= swf_InsertTag(0, ST_PLACEOBJECT2);
768     swf_SetPlaceObject(tag->tag, po);
769     mylog("PlaceObject %08x -> %08x\n", (int)self, (int)tag);
770     return (PyObject*)tag;
771 }
772 //----------------------------------------------------------------------------
773 static PyTypeObject SWFClass = 
774 {
775     PyObject_HEAD_INIT(NULL)
776     0,
777     tp_name: "SWF",
778     tp_basicsize: sizeof(SWFObject),
779     tp_itemsize: 0,
780     tp_dealloc: swf_dealloc,
781     tp_print: swf_print,
782     tp_getattr: swf_getattr,
783     tp_setattr: swf_setattr,
784 };
785 static PySequenceMethods taglist_as_sequence =
786 {
787     sq_length: taglist_length, // len(obj)
788     sq_concat: taglist_concat, // obj += [...], obj1+obj2
789     sq_repeat: 0,            // x*n, intargfunc
790     sq_item: taglist_item,  // obj[3]
791     sq_slice: 0,             // x[i:j] intintargfunc
792     sq_ass_item: 0,          // x[i] = y intobjargproc
793     sq_ass_slice: 0,         // x[i:j] = v intintobjargproc
794     sq_contains: 0,          //???
795 };
796 static PyTypeObject TagListClass = 
797 {
798     PyObject_HEAD_INIT(NULL)
799     0,
800     tp_name: "TagList",
801     tp_basicsize: sizeof(TagListObject),
802     tp_itemsize: 0,
803     tp_dealloc: taglist_dealloc,
804     tp_print: 0,                 // print x
805     tp_getattr: taglist_getattr, // x.attr
806     tp_setattr: 0,               // x.attr = v
807     tp_compare: 0,               // x>y
808     tp_repr: 0,                  // `x`, print x
809     tp_as_number: 0,
810     tp_as_sequence: &taglist_as_sequence,
811 };
812 static PyTypeObject TagClass = 
813 {
814     PyObject_HEAD_INIT(NULL)
815     0,
816     tp_name: "Tag",
817     tp_basicsize: sizeof(TagObject),
818     tp_itemsize: 0,
819     tp_dealloc: tag_dealloc,
820     tp_print: 0,
821     tp_getattr: tag_getattr,
822 };
823 //----------------------------------------------------------------------------
824
825 static PyObject* module_verbose(PyObject* self, PyObject* args)
826 {
827     if (!PyArg_ParseTuple(args,"i", &verbose)) 
828         return NULL;
829     return Py_BuildValue("s", 0);
830 }
831
832
833 static PyMethodDef SWFMethods[] = 
834 {
835     /* Module functions */
836     {"verbose", module_verbose, METH_VARARGS, "Set the module verbosity"},
837
838     /* SWF creation*/
839     {"load", f_load, METH_VARARGS, "Load a SWF from disc."},
840     {"create", (PyCFunction)f_create, METH_KEYWORDS, "Create a new SWF from scratch."},
841     
842     /* Primitives */
843     {"Color", (PyCFunction)f_Color, METH_KEYWORDS, "Create a new color object."},
844     {"Gradient", (PyCFunction)f_Gradient, METH_KEYWORDS, "Create a new gradient object."},
845     {"ColorTransform", (PyCFunction)f_ColorTransform, METH_KEYWORDS, "Create a new colortransform object."},
846     {"Matrix", (PyCFunction)f_Matrix, METH_KEYWORDS, "Create a new matrix object."},
847     {"BBox", (PyCFunction)f_BBox, METH_KEYWORDS, "Create a new bounding box object."},
848
849     /* TAGS */
850     {"SetBackgroundColor", (PyCFunction)f_SetBackgroundColor, METH_KEYWORDS, "Create a SetBackGroundColor Tag."},
851     {"Protect", (PyCFunction)f_Protect, METH_KEYWORDS, "Create a Protect Tag."},
852     {"DefineFont", (PyCFunction)f_DefineFont, METH_KEYWORDS, "Create a DefineFont Tag."},
853     {"DefineText", (PyCFunction)f_DefineText, METH_KEYWORDS, "Create a DefineText Tag."},
854     {"PlaceObject", (PyCFunction)f_PlaceObject, METH_KEYWORDS, "Create a PlaceObject Tag."},
855     {NULL, NULL, 0, NULL}
856 };
857
858 void initSWF(void)
859 {
860     SWFClass.ob_type = &PyType_Type;
861
862     initLog("test.log",8,0,0,0,0);
863
864     (void)Py_InitModule("SWF", SWFMethods);
865 }