9d69de2f50b8db6452c07ec19ddf918d3b4bbce4
[swftools.git] / lib / python / tag.c
1 #include <Python.h>
2 #undef HAVE_STAT
3 #include "../rfxswf.h"
4 #include "../log.h"
5 #include "./pyutils.h"
6 #include "primitives.h"
7 #include "action.h"
8 #include "tag.h"
9 #include "tagmap.h"
10
11 //----------------------------------------------------------------------------
12
13 struct _TagObject;
14
15 typedef struct _tag_internals
16 {
17     int size;
18     int (*parse)(struct _TagObject*);
19     int (*fillTAG)(struct _TagObject*);
20     void (*dealloc)(struct _TagObject*);
21     void*data;
22     PyMethodDef* tagfunctions;
23 } tag_internals_t;
24
25 typedef struct _TagObject {
26     PyObject_HEAD
27     
28     PyObject* tagmap;
29     TAG*tag;
30     tag_internals_t internals;
31 } TagObject;
32
33 //----------------------------------------------------------------------------
34
35 PyObject* tag_new(tag_internals_t*tag_internals);
36    
37 typedef struct _font_internal
38 {
39     SWFFONT* font;
40 } font_internal_t;
41 staticforward tag_internals_t font_tag;
42
43 static int font_parse(TagObject*self)
44 {
45     font_internal_t*font = (font_internal_t*)self->internals.data;
46     /* TODO */
47     PyErr_SetString(PyExc_Exception, setError("Font parsing not implemented yet"));
48     return 0;
49 }
50 static void font_dealloc(TagObject*self)
51 {
52     font_internal_t*font = (font_internal_t*)self->internals.data;
53     if(font->font) {
54         swf_FontFree(font->font);
55         font->font = 0;
56     }
57 }
58 static int font_fillTAG(TagObject*self)
59 {
60     font_internal_t*fi = (font_internal_t*)self->internals.data;
61     if(self->tag)
62         return 1;
63     self->tag = swf_InsertTag(0, ST_DEFINEFONT2);
64     swf_FontSetDefine2(self->tag, fi->font);
65     return 1;
66 }
67 static PyObject* f_DefineFont(PyObject* self, PyObject* args, PyObject* kwargs)
68 {
69     static char *kwlist[] = {"filename", NULL};
70     char*filename = 0;
71     TagObject*tag;
72     SWFFONT* font;
73
74     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &filename))
75         return NULL;
76
77     font = swf_LoadFont(filename);
78     if(!font) {
79         PyErr_SetString(PyExc_Exception, setError("Could not load %s", filename));
80         return NULL;
81     }
82
83     tag = (TagObject*)tag_new(&font_tag);
84     font_internal_t*fi = (font_internal_t*)tag->internals.data;
85     font->id = 0;
86     fi->font = font;
87     return (PyObject*)tag;
88 }
89 static SWFFONT* font_getSWFFONT(PyObject*self)
90 {
91     PY_ASSERT_TYPE(self, &TagClass);
92     TagObject*tag = (TagObject*)self;
93     font_internal_t*fi = (font_internal_t*)tag->internals.data;
94     return fi->font;
95 }
96 static tag_internals_t font_tag =
97 {
98     size: sizeof(font_internal_t),
99     parse: font_parse,
100     fillTAG: font_fillTAG,
101     dealloc: font_dealloc,
102     tagfunctions: 0
103 };
104 //----------------------------------------------------------------------------
105
106 typedef struct _placeobject_internal
107 {
108     SWFPLACEOBJECT* po;
109 } placeobject_internal_t;
110 staticforward tag_internals_t placeobject_tag;
111
112 static void po_dealloc(TagObject*self)
113 {
114     placeobject_internal_t*pi = (placeobject_internal_t*)self->internals.data;
115     if(pi->po) {
116         swf_PlaceObjectFree(pi->po);
117         pi->po = 0;
118     }
119 }
120 static int po_parse(TagObject*self)
121 {
122     placeobject_internal_t*pi = (placeobject_internal_t*)self->internals.data;
123     /* TODO */
124     PyErr_SetString(PyExc_Exception, setError("Font parsing not implemented yet"));
125     return 0;
126 }
127 static int po_fillTAG(TagObject*self)
128 {
129     placeobject_internal_t*pi = (placeobject_internal_t*)self->internals.data;
130     self->tag = swf_InsertTag(0, ST_PLACEOBJECT2);
131     swf_SetPlaceObject(self->tag, pi->po);
132     return 1;
133 }
134 static PyObject* f_PlaceObject(PyObject* self, PyObject* args, PyObject* kwargs)
135 {
136     static char *kwlist[] = {"character", "depth", "matrix", "colortransform", "ratio", "name", "clipdepth", "action", NULL};
137     TagObject*tag;
138     
139     TagObject*character = 0;
140     int depth;
141     int clipdepth = 0;
142     PyObject*matrix = 0;
143     PyObject*cxform = 0;
144     PyObject*action = 0;
145     int ratio = 0;
146     char* name = 0;
147     SWFPLACEOBJECT* po;
148     po = malloc(sizeof(SWFPLACEOBJECT));
149     memset(po, 0, sizeof(SWFPLACEOBJECT));
150
151     swf_GetPlaceObject(0, po);
152
153     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!i|O!O!isiO!", kwlist, 
154                 &TagClass, &character, 
155                 &depth, 
156                 &MatrixClass, &matrix, 
157                 &CXFormClass, &cxform,
158                 &ratio,
159                 &name,
160                 &clipdepth,
161                 &ActionClass, &action
162                 ))
163         return NULL;
164     po->depth = depth;
165     po->id = /*ID*/ 0;
166     po->clipdepth = clipdepth;
167     po->ratio = ratio;
168     po->name = name;
169     if(clipdepth) po->clipdepth = clipdepth;
170     if(matrix) po->matrix = matrix_getMatrix(matrix);
171     if(cxform) po->cxform = colortransform_getCXForm(cxform);
172     if(action) po->actions = action_getAction(action);
173
174     tag = (TagObject*)tag_new(&placeobject_tag);
175     placeobject_internal_t*pi = (placeobject_internal_t*)tag->internals.data;
176     pi->po = po;
177     pi->po->id = tagmap_add(tag->tagmap,(PyObject*)character);
178     
179     mylog("+%08x(%d) PlaceObject %08x(%d)\n", (int)tag, tag->ob_refcnt, character, character->ob_refcnt);
180
181     return (PyObject*)tag;
182 }
183 static tag_internals_t placeobject_tag =
184 {
185     size: sizeof(placeobject_internal_t),
186     parse: po_parse,
187     fillTAG: po_fillTAG,
188     dealloc: po_dealloc,
189     tagfunctions: 0
190 };
191 //----------------------------------------------------------------------------
192 staticforward tag_internals_t bgcolor_tag;
193 static PyObject* tag_setbackgroundcolor_getrgb(PyObject * self, PyObject*other)
194 {
195     TagObject*tag = (TagObject*)self;
196     int r,g,b;
197     r = tag->tag->data[0];
198     g = tag->tag->data[1];
199     b = tag->tag->data[2];
200     return Py_BuildValue("(iii)", r,g,b);
201 }
202 static PyMethodDef setbgcolor_methods[] = 
203 {{"getRGB", tag_setbackgroundcolor_getrgb, METH_VARARGS, "get's the color set by this tag"},
204  {NULL, NULL, 0, NULL}
205 };
206 static PyObject* f_SetBackgroundColor(PyObject* self, PyObject* args, PyObject* kwargs)
207 {
208     static char *kwlist[] = {"color", NULL};
209     int r=0,g=0,b=0;
210     TagObject*tag;
211     PyObject*color;
212     
213     tag = (TagObject*)tag_new(&bgcolor_tag);
214
215     /* 1st try- copy constructor */
216     if(!PyArg_ParseTupleAndKeywords(args, kwargs, "O!", kwlist, &ColorClass, &color)) {
217         /* 2nd try- color's contructor */
218         color = f_Color(NULL, args, kwargs);
219     }
220     if(!color)
221         return NULL;
222
223     tag->tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
224     RGBA rgba = color_getRGBA(color);
225     swf_SetU8(tag->tag, rgba.r);
226     swf_SetU8(tag->tag, rgba.g);
227     swf_SetU8(tag->tag, rgba.b);
228     mylog(" %08x(%d) SetBackgroundColor(%02x,%02x,%02x) (colorobj=%08x(%d))\n", (int)tag, tag->ob_refcnt, rgba.r, rgba.g, rgba.b, color, color->ob_refcnt);
229     return (PyObject*)tag;
230 }
231 static tag_internals_t bgcolor_tag =
232 {
233     size: 0,
234     parse: 0,
235     fillTAG: 0,
236     dealloc: 0,
237     tagfunctions: setbgcolor_methods
238 };
239 //----------------------------------------------------------------------------
240 staticforward tag_internals_t protect_tag;
241 static PyObject* f_Protect(PyObject* self, PyObject* args, PyObject* kwargs)
242 {
243     static char *kwlist[] = {"password", NULL};
244     char*password = 0;
245
246     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &password))
247         return NULL;
248
249     TagObject*tag = (TagObject*)tag_new(&protect_tag);
250     tag->tag = swf_InsertTag(0, ST_PROTECT);
251     if(password) {
252         swf_SetPassword(tag->tag, password);
253     }
254     mylog("+%08x(%d) f_Protect", (int)tag, tag->ob_refcnt);
255     return (PyObject*)tag;
256 }
257 static tag_internals_t protect_tag =
258 {
259     size: 0,
260     parse: 0,
261     fillTAG: 0,
262     dealloc: 0,
263     tagfunctions: 0
264 };
265 //----------------------------------------------------------------------------
266 staticforward tag_internals_t text_tag;
267
268 typedef struct _text_internal // not used yet
269 {
270     int font_id;
271     char*text;
272     SWFFONT* swffont;
273     RGBA rgba;
274     int size;
275 } text_internal_t;
276 staticforward tag_internals_t placeobject_tag;
277
278 static int text_fillTAG(TagObject*self) //not used yet
279 {
280     text_internal_t*ti = (text_internal_t*)self->internals.data;
281     self->tag= swf_InsertTag(0, ST_DEFINETEXT2);
282     swf_SetU16(self->tag, /*ID*/0);
283     SRECT r = swf_SetDefineText(self->tag, ti->swffont, &ti->rgba, ti->text, ti->size);
284     return 0;
285 }
286 static PyObject* f_DefineText(PyObject* self, PyObject* args, PyObject* kwargs)
287 {
288     static char *kwlist[] = {"font", "text", "size", "color", NULL};
289     TagObject*tag = 0;
290     char*text = 0;
291     int textlen = 0;
292     PyObject*unicode16;
293     PyObject*unicode8;
294     int size = 0;
295     RGBA rgba = {255,0,0,0};
296     PyObject*color = 0;
297     PyObject*font = 0;
298     SRECT r;
299
300     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!u#i|O!", kwlist, &TagClass, &font, &text, &textlen, &size, &ColorClass, &color))
301         return NULL;
302     
303     unicode16 = PyUnicode_DecodeUTF16(text, textlen*2, NULL, NULL);
304     unicode8 = PyUnicode_AsUTF8String(unicode16);
305     text = PyString_AS_STRING(unicode8);
306
307     if(color)
308         rgba = color_getRGBA(color);
309
310     mylog("DefineText: text = %s", text);
311     
312     tag = (TagObject*)tag_new(&text_tag);
313     text_internal_t*ti = (text_internal_t*)tag->internals.data;
314
315     SWFFONT* swffont = font_getSWFFONT(font);
316     int font_id = tagmap_add(tag->tagmap, font); // add dependency on font
317     swffont->id = font_id; // for swf_SetDefineText
318
319     /* todo: remove, store data in ti */
320     tag->tag= swf_InsertTag(0, ST_DEFINETEXT2);
321     swf_SetU16(tag->tag, /*ID*/0);
322     r = swf_SetDefineText(tag->tag, swffont, &rgba, text, size);
323     mylog("+%08x(%d) DefineText tag=%08x \n", (int)tag, tag->ob_refcnt);
324     
325     //Py_DECREF(unicode16);
326     //Py_DECREF(unicode8);
327     //free(text);
328
329     return (PyObject*)tag;
330 }
331 static tag_internals_t text_tag =
332 {
333     size: sizeof(text_internal_t),
334     parse: 0,
335     fillTAG: text_fillTAG,
336     dealloc: 0,
337     tagfunctions: 0
338 };
339 //----------------------------------------------------------------------------
340 static PyMethodDef generic_methods[] = 
341 {
342   {NULL, NULL, 0, NULL}
343 };
344 static tag_internals_t generic_tag =
345 {
346     size: 0,
347     parse: 0,
348     dealloc: 0,
349     fillTAG: 0,
350     tagfunctions: generic_methods
351 };
352 //----------------------------------------------------------------------------
353
354 struct tag_parser {
355     int id;
356     tag_internals_t*spec;
357 } tag_parsers[] = {
358     {ST_PLACEOBJECT,&placeobject_tag},
359     {ST_PLACEOBJECT2,&placeobject_tag},
360     {ST_SETBACKGROUNDCOLOR,&bgcolor_tag},
361     {ST_DEFINEFONT,&font_tag},
362     {ST_PROTECT,&protect_tag},
363     {ST_DEFINETEXT,&text_tag},
364     {-1,0}
365 };
366 //----------------------------------------------------------------------------
367 static void tag_dealloc(PyObject * self)
368 {
369     TagObject*tag = (TagObject*)self;
370     if(tag->tag)
371         mylog("-%08x(%d) tag_dealoc [%s]\n", (int)self, self->ob_refcnt, swf_TagGetName(tag->tag));
372     else
373         mylog("-%08x(%d) tag_dealoc [?]\n", (int)self, self->ob_refcnt);
374     if(tag->internals.dealloc) {
375         if(!tag->internals.data)
376             mylog("-%08x(%d) tag_dealoc: Warning: calling dealloc without any data(?)\n", (int)self, self->ob_refcnt);
377         tag->internals.dealloc(tag);
378     }
379     if(tag->internals.data) {
380         free(tag->internals.data);
381         tag->internals.data = 0;
382     }
383     if(tag->tag) {
384         swf_DeleteTag(tag->tag);
385         tag->tag = 0;
386     }
387     Py_DECREF(tag->tagmap);
388     tag->tagmap = 0;
389     PyObject_Del(self);
390 }
391 //----------------------------------------------------------------------------
392 static PyObject* tag_setU8(PyObject * self, PyObject*other)
393 {
394     return NULL;
395 }
396 //----------------------------------------------------------------------------
397 static PyMethodDef common_tagfunctions[] =
398 {{"setU8", tag_setU8, METH_VARARGS, "sets a byte to the tag data"},
399  {NULL, NULL, 0, NULL}
400 };
401
402 static int fillTAG(PyObject*self) 
403 {
404     TagObject*tag = (TagObject*)self;
405     if(tag->tag)
406         return 1;
407     if(!tag->internals.fillTAG) {
408         PyErr_SetString(PyExc_Exception, setError("No way to fill TAG with data"));
409         return 0;
410     }
411     if(!tag->internals.fillTAG(tag)) {
412         return 0; // pass through exception
413     }
414     if(!tag->tag) {
415         PyErr_SetString(PyExc_Exception, setError("Couldn't fill tag"));
416         return 0;
417     }
418     return 1;
419 }
420 static PyObject* tag_getattr(PyObject * self, char* a)
421 {
422     TagObject*tag = (TagObject*)self;
423     PyObject* ret = NULL;
424     int t;
425
426     /* -- fields -- */
427     if(!strcmp(a, "id")) {
428         if(!fillTAG(self))
429             return 0;
430         return Py_BuildValue("i", tag->tag->id);
431     }
432     if(!strcmp(a, "name")) {
433         if(!fillTAG(self))
434             return 0;
435         char* name = swf_TagGetName(tag->tag);
436         return Py_BuildValue("s", name);
437     }
438     
439     /* search for a tag specific function */
440     if(tag->internals.tagfunctions) {
441         mylog(" %08x(%d) tag_getattr: tag has specific functions\n", (int)self, self->ob_refcnt);
442         ret = Py_FindMethod(tag->internals.tagfunctions, self, a);
443         if(!ret) return ret;
444         ret = FindMethodMore(ret, common_tagfunctions, self, a);
445         mylog(" %08x(%d) tag_getattr %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
446         return ret;
447     }
448    
449     ret = Py_FindMethod(common_tagfunctions, self, a);
450
451     mylog(" %08x(%d) tag_getattr %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
452     return ret;
453 }
454 //----------------------------------------------------------------------------
455 //                     Tag Constructors
456 //----------------------------------------------------------------------------
457 PyObject* tag_new(tag_internals_t*tag_internals)
458 {
459     TagObject*tag = PyObject_New(TagObject, &TagClass);
460     mylog("+%08x(%d) tag_new\n", (int)tag, tag->ob_refcnt);
461     memcpy(&tag->internals, tag_internals, sizeof(tag_internals_t));
462     if(tag->internals.size) {
463         tag->internals.data = malloc(tag->internals.size);
464         memset(tag->internals.data , 0, tag->internals.size);
465     } else {
466         tag->internals.data = 0;
467     }
468     tag->tag = 0;
469     tag->tagmap = tagmap_new();
470
471     return (PyObject*)tag;
472 }
473 PyObject* tag_new2(TAG*t, PyObject* tagmap)
474 {
475     TagObject*tag = PyObject_New(TagObject, &TagClass);
476     mylog("+%08x(%d) tag_new2 tag=%08x id=%d (%s)\n", (int)tag, tag->ob_refcnt, t, t->id, swf_TagGetName(t));
477     
478     tag->tagmap = tagmap_new();
479
480     int num = swf_GetNumUsedIDs(t);
481     if(num) { // tag has dependencies
482         int * positions = malloc(num*sizeof(int));
483         swf_GetUsedIDs(t, positions);
484         int i;
485         for(i=0;i<num;i++) {
486             int id = GET16(&t->data[positions[i]]);
487             PyObject*obj = tagmap_id2obj(tagmap, id);
488             if(obj==NULL) {
489                 PyErr_SetString(PyExc_Exception, setError("TagID %d not defined", id));
490                 return NULL;
491             }
492             //mylog("+%08x(%d) tag_new2 handling id %d at %d/%d\n", (int)tag, tag->ob_refcnt, id, positions[i], t->len);
493             //mylog("+%08x(%d) tag_new2 add dependency %d to id %d, object %08x(%d)\n", (int)tag, tag->ob_refcnt, i, id, obj, obj->ob_refcnt);
494             tagmap_addMapping(tag->tagmap, id, obj);
495         }
496         free(positions);
497     }
498
499     tag->tag = swf_InsertTag(0, t->id);
500     swf_SetBlock(tag->tag, t->data, t->len);
501
502     int i=0,found=0;
503     while(tag_parsers[i].id>=0) {
504         if(tag_parsers[i].id == t->id) {
505             memcpy(&tag->internals, tag_parsers[i].spec, sizeof(tag_internals_t));
506             found=1;
507             break;
508         }
509         i++;
510     }
511     if(!found) {
512         memcpy(&tag->internals, &generic_tag, sizeof(tag_internals_t));
513     }
514     if(tag->internals.size) {
515         tag->internals.data = malloc(tag->internals.size);
516         memset(tag->internals.data, 0, tag->internals.size);
517     } else {
518         tag->internals.data = 0;
519     }
520     return (PyObject*)tag;
521 }
522 //----------------------------------------------------------------------------
523 /* serialize */
524 TAG* tag_getTAG(PyObject*self, TAG*prevTag, PyObject*tagmap)
525 {
526     TagObject*tag = (TagObject*)self;
527
528     if(!fillTAG(self))
529         return 0;
530     mylog(" %08x(%d) tag_getTAG: tag=%08x id=%d (%s)", (int)self, self->ob_refcnt, tag->tag, tag->tag->id, swf_TagGetName(tag->tag));
531
532     TAG* t = swf_InsertTag(prevTag, tag->tag->id);
533     swf_SetBlock(t, tag->tag->data, tag->tag->len);
534     
535     if(swf_isDefiningTag(t)) {
536         int newid = tagmap_add(tagmap, self);
537         swf_SetDefineID(t, newid);
538     }
539
540     int num = swf_GetNumUsedIDs(t);
541     if(num) { // tag has dependencies
542         int * positions = malloc(num*sizeof(int));
543         swf_GetUsedIDs(t, positions);
544         int i;
545         for(i=0;i<num;i++) {
546             int id = GET16(&t->data[positions[i]]);
547             PyObject* obj =  tagmap_id2obj(tag->tagmap, id);
548             if(obj==NULL) {
549                 PyErr_SetString(PyExc_Exception, setError("Internal error: id %d not known in taglist:"));
550                 free(positions);
551                 return 0;
552             }
553             int newid = tagmap_obj2id(tagmap, obj);
554             PUT16(&t->data[positions[i]], newid);
555         }
556         free(positions);
557     }
558     return t;
559 }
560 PyObject* tag_getDependencies(PyObject*self)
561 {
562     TagObject*tag = (TagObject*)self;
563     mylog(" %08x(%d) tag_getDependencies\n", (int)self, self->ob_refcnt);
564     return tagmap_getObjectList(tag->tagmap);
565 }
566 int tag_print(PyObject * self, FILE * fi, int flags)
567 {
568     TagObject*tag = (TagObject*)self;
569     mylog(" %08x(%d) tag_print flags=%08x\n", (int)self, self->ob_refcnt, flags);
570     if(!fillTAG(self))
571         return -1;
572     fprintf(fi, "tag-%08x-%d-%s", (int)tag->tag, tag->tag->id, swf_TagGetName(tag->tag));
573     return 0;
574 }
575 PyTypeObject TagClass = 
576 {
577     PyObject_HEAD_INIT(NULL)
578     0,
579     tp_name: "Tag",
580     tp_basicsize: sizeof(TagObject),
581     tp_itemsize: 0,
582     tp_dealloc: tag_dealloc,
583     tp_print: tag_print,
584     tp_getattr: tag_getattr,
585 };
586 static PyMethodDef TagMethods[] = 
587 {
588     /* TAGS */
589     {"SetBackgroundColor", (PyCFunction)f_SetBackgroundColor, METH_KEYWORDS, "Create a SetBackGroundColor Tag."},
590     {"Protect", (PyCFunction)f_Protect, METH_KEYWORDS, "Create a Protect Tag."},
591     {"DefineFont", (PyCFunction)f_DefineFont, METH_KEYWORDS, "Create a DefineFont Tag."},
592     {"DefineText", (PyCFunction)f_DefineText, METH_KEYWORDS, "Create a DefineText Tag."},
593     {"PlaceObject", (PyCFunction)f_PlaceObject, METH_KEYWORDS, "Create a PlaceObject Tag."},
594     {NULL, NULL, 0, NULL}
595 };
596 PyMethodDef* tag_getMethods()
597 {
598     TagClass.ob_type = &PyType_Type;
599     return TagMethods;
600 }
601