more horizontal refactoring
[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 typedef struct _TagObject {
14     PyObject_HEAD
15     tag_internals_t internals;
16 } TagObject;
17
18 //----------------------------------------------------------------------------
19 static PyMethodDef generic_methods[] = 
20 {
21   {NULL, NULL, 0, NULL}
22 };
23 static tag_internals_t generic_tag =
24 {
25     parse: 0,
26     dealloc: 0,
27     fillTAG: 0,
28     tagfunctions: generic_methods,
29     datasize: 0,
30 };
31 //----------------------------------------------------------------------------
32
33 static struct tag_parser {
34     int id;
35     tag_internals_t*spec;
36     struct tag_parser* next;
37 } tag_parsers[1024];
38 static char parsers_initialized = 0;
39
40 void register_tag(int id, tag_internals_t*spec)
41 {
42     assert(id>=0 && id<1024);
43     if(!parsers_initialized) {
44         memset(tag_parsers, 0, sizeof(tag_parsers));
45         parsers_initialized = 1;
46     }
47     tag_parsers[id].id = id;
48     tag_parsers[id].spec = spec;
49 };
50
51 static tag_internals_t* get_parser(int id)
52 {
53     if(parsers_initialized<2) {
54         int t;
55         struct tag_parser*last = &tag_parsers[0];
56         for(t=0;t<1024;t++) {
57             if(tag_parsers[t].spec) {
58                 last->next = &tag_parsers[t];
59                 last = &tag_parsers[t];
60             }
61         }
62         parsers_initialized = 2;
63     }
64     assert(id>=0 && id<1024);
65     return tag_parsers[id].spec;
66 }
67
68 //----------------------------------------------------------------------------
69 static void tag_dealloc(PyObject * self)
70 {
71     TagObject*tag = (TagObject*)self;
72     if(tag->internals.tag)
73         mylog("-%08x(%d) tag_dealoc [%s]\n", (int)self, self->ob_refcnt, swf_TagGetName(tag->internals.tag));
74     else
75         mylog("-%08x(%d) tag_dealoc [?]\n", (int)self, self->ob_refcnt);
76     if(tag->internals.dealloc) {
77         if(!tag->internals.data)
78             mylog("-%08x(%d) tag_dealoc: Warning: calling dealloc without any data(?)\n", (int)self, self->ob_refcnt);
79         tag->internals.dealloc(&tag->internals);
80     }
81     if(tag->internals.data) {
82         free(tag->internals.data);
83         tag->internals.data = 0;
84     }
85     if(tag->internals.tag) {
86         swf_DeleteTag(0, tag->internals.tag);
87         tag->internals.tag = 0;
88     }
89     Py_DECREF(tag->internals.tagmap);
90     tag->internals.tagmap = 0;
91     PyObject_Del(self);
92 }
93 //----------------------------------------------------------------------------
94 static int fillTAG(PyObject*self) 
95 {
96     TagObject*tag = (TagObject*)self;
97     if(tag->internals.tag)
98         return 1;
99     if(!tag->internals.fillTAG) {
100         PyErr_SetString(PyExc_Exception, setError("No way to fill TAG with data"));
101         return 0;
102     }
103     if(!tag->internals.fillTAG(&tag->internals)) {
104         return 0; // pass through exception
105     }
106     if(!tag->internals.tag) {
107         PyErr_SetString(PyExc_Exception, setError("Couldn't fill tag"));
108         return 0;
109     }
110     return 1;
111 }
112 //----------------------------------------------------------------------------
113 static PyObject* tag_isShape(PyObject * _self, PyObject*args)
114 {
115     TagObject*self = (TagObject*)_self;
116     if(!PyArg_ParseTuple(args, "")) return NULL;
117     if(!fillTAG((PyObject*)self))   return NULL;
118     return PyInt_FromLong(swf_isShapeTag(self->internals.tag));
119 }
120 static PyObject* tag_isFont(PyObject * _self, PyObject*args)
121 {
122     TagObject*self = (TagObject*)_self;
123     if(!PyArg_ParseTuple(args, "")) return NULL;
124     if(!fillTAG((PyObject*)self))   return NULL;
125     int id = self->internals.tag->id;
126     int isfont=0;
127     if(id == ST_DEFINEFONT || id == ST_DEFINEFONT2)
128         isfont = 1;
129     return PyInt_FromLong(isfont);
130 }
131 static PyObject* tag_isImage(PyObject * _self, PyObject*args)
132 {
133     TagObject*self = (TagObject*)_self;
134     if(!PyArg_ParseTuple(args, "")) return NULL;
135     if(!fillTAG((PyObject*)self))   return NULL;
136     return PyInt_FromLong(swf_isImageTag(self->internals.tag));
137 }
138 static PyObject* tag_isDefiningTag(PyObject * _self, PyObject*args)
139 {
140     TagObject*self = (TagObject*)_self;
141     if(!PyArg_ParseTuple(args, "")) return NULL;
142     if(!fillTAG((PyObject*)self))   return NULL;
143     return PyInt_FromLong(swf_isDefiningTag(self->internals.tag));
144 }
145 static PyObject* tag_isPlacement(PyObject * _self, PyObject*args)
146 {
147     TagObject*self = (TagObject*)_self;
148     if(!PyArg_ParseTuple(args, "")) return NULL;
149     if(!fillTAG((PyObject*)self))   return NULL;
150     return PyInt_FromLong((self->internals.tag->id == ST_PLACEOBJECT ||
151                            self->internals.tag->id == ST_PLACEOBJECT2));
152 }
153 static PyObject* tag_getBBox(PyObject * _self, PyObject*args)
154 {
155     TagObject*self = (TagObject*)_self;
156     if(!PyArg_ParseTuple(args, "")) return NULL;
157     if(!fillTAG((PyObject*)self))   return NULL;
158     return f_BBox2(swf_GetDefineBBox(self->internals.tag));
159 }
160 static PyObject* tag_setBBox(PyObject * _self, PyObject*args)
161 {
162     TagObject*self = (TagObject*)_self;
163     PyObject*bbox = 0;
164     if(!PyArg_ParseTuple(args, "O!", &BBoxClass, &bbox)) return NULL;
165     if(!fillTAG((PyObject*)self))   return NULL;
166     swf_SetDefineBBox(self->internals.tag, bbox_getSRECT(bbox));
167     return PY_NONE;
168 }
169 //----------------------------------------------------------------------------
170 static PyMethodDef common_tagfunctions[] =
171 {{"isShape", tag_isShape, METH_VARARGS, "tests whether the tag is a shape tag"},
172  {"isImage", tag_isImage, METH_VARARGS, "tests whether the tag is an image"},
173  {"isFont", tag_isFont, METH_VARARGS, "tests whether the tag is a font"},
174  {"isDefiningTag", tag_isDefiningTag, METH_VARARGS, "tests whether the tag is a defining tag"},
175  {"isPlacement", tag_isPlacement, METH_VARARGS, "tests whether the tag is a placement"},
176  {"getBBox", tag_getBBox, METH_VARARGS, "get's the tags bounding box"},
177  {"setBBox", tag_setBBox, METH_VARARGS, "set's the tags bounding box"},
178  {NULL, NULL, 0, NULL}
179 };
180
181 static PyObject* tag_getattr(PyObject * self, char* a)
182 {
183     TagObject*tag = (TagObject*)self;
184     PyObject* ret = NULL;
185     int t;
186
187     /* -- fields -- */
188     if(!strcmp(a, "tagid")) {
189         if(!fillTAG(self))
190             return 0;
191         return Py_BuildValue("i", tag->internals.tag->id);
192     }
193     if(!strcmp(a, "name")) {
194         if(!fillTAG(self))
195             return 0;
196         char* name = swf_TagGetName(tag->internals.tag);
197         return Py_BuildValue("s", name);
198     }
199     if(!strcmp(a, "data")) {
200         if(!fillTAG(self))
201             return 0;
202         return Py_BuildValue("s#", tag->internals.tag->data, tag->internals.tag->len);
203     }
204     if(tag->internals.getattr) {
205         PyObject* ret = tag->internals.getattr(&tag->internals, a);
206         if(ret) return ret;
207     }
208     
209     /* search for a tag specific function */
210     if(tag->internals.tagfunctions) {
211         mylog(" %08x(%d) tag_getattr: tag has specific functions\n", (int)self, self->ob_refcnt);
212         ret = Py_FindMethod(tag->internals.tagfunctions, self, a);
213         if(ret) return ret;
214         PyErr_Clear();
215         ret = FindMethodMore(ret, common_tagfunctions, self, a);
216         mylog(" %08x(%d) tag_getattr %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
217         if(ret) return ret;
218         PyErr_Clear();
219     }
220   
221     ret = Py_FindMethod(common_tagfunctions, self, a);
222
223     mylog(" %08x(%d) tag_getattr %s: %08x\n", (int)self, self->ob_refcnt, a, ret);
224     return ret;
225 }
226 static int tag_setattr(PyObject * _self, char* a, PyObject * o)
227 {
228     TagObject*self= (TagObject*)_self;
229     /* a setattr will almost certainly change the tag data,
230        so delete the tag */
231     if(self->internals.tag) {
232         swf_DeleteTag(0, self->internals.tag);
233         self->internals.tag = 0;
234     }
235     if(self->internals.setattr) {
236         int ret = self->internals.setattr(&self->internals, a, o);
237         return ret;
238     }
239     return 1;
240 }
241 //----------------------------------------------------------------------------
242 //                     Tag Constructors
243 //----------------------------------------------------------------------------
244 PyObject* tag_new(tag_internals_t*tag_internals)
245 {
246     TagObject*tag = PyObject_New(TagObject, &TagClass);
247     mylog("+%08x(%d) tag_new\n", (int)tag, tag->ob_refcnt);
248     memcpy(&tag->internals, tag_internals, sizeof(tag_internals_t));
249     if(tag->internals.datasize) {
250         tag->internals.data = malloc(tag->internals.datasize);
251         memset(tag->internals.data , 0, tag->internals.datasize);
252     } else {
253         tag->internals.data = 0;
254     }
255     tag->internals.tag = 0;
256     tag->internals.tagmap = tagmap_new();
257
258     return (PyObject*)tag;
259 }
260 PyObject* tag_new2(TAG*t, PyObject* tagmap)
261 {
262     TagObject*tag = PyObject_New(TagObject, &TagClass);
263     mylog("+%08x(%d) tag_new2 tag=%08x id=%d (%s)\n", (int)tag, tag->ob_refcnt, t, t->id, swf_TagGetName(t));
264     
265     PyObject*mytagmap = tagmap_new();
266
267     int num = swf_GetNumUsedIDs(t);
268     if(num) { // tag has dependencies
269         int * positions = malloc(num*sizeof(int));
270         swf_GetUsedIDs(t, positions);
271         int i;
272         for(i=0;i<num;i++) {
273             int id = GET16(&t->data[positions[i]]);
274             PyObject*obj = tagmap_id2obj(tagmap, id);
275             if(obj==NULL) {
276                 PyErr_SetString(PyExc_Exception, setError("TagID %d not defined", id));
277                 return NULL;
278             }
279             //mylog("+%08x(%d) tag_new2 handling id %d at %d/%d\n", (int)tag, tag->ob_refcnt, id, positions[i], t->len);
280             //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);
281             tagmap_addMapping(mytagmap, id, obj);
282         }
283         free(positions);
284     }
285
286     tag_internals_t*spec = get_parser(t->id);
287     if(spec) {
288         memcpy(&tag->internals, spec, sizeof(tag_internals_t));
289     } else {
290         memcpy(&tag->internals, &generic_tag, sizeof(tag_internals_t));
291     }
292     if(tag->internals.datasize) {
293         tag->internals.data = malloc(tag->internals.datasize);
294         memset(tag->internals.data, 0, tag->internals.datasize);
295     } else {
296         tag->internals.data = 0;
297     }
298     tag->internals.tag = swf_InsertTag(0, t->id);
299     swf_SetBlock(tag->internals.tag, t->data, t->len);
300     tag->internals.tagmap = mytagmap;
301
302     // call tag->internals.init()?
303
304     return (PyObject*)tag;
305 }
306 //----------------------------------------------------------------------------
307 /* serialize */
308 TAG* tag_getTAG(PyObject*self, TAG*prevTag, PyObject*tagmap)
309 {
310     TagObject*tag = (TagObject*)self;
311
312     if(!fillTAG(self))
313         return 0;
314     mylog(" %08x(%d) tag_getTAG: tag=%08x id=%d (%s)", (int)self, self->ob_refcnt, tag->internals.tag, tag->internals.tag->id, swf_TagGetName(tag->internals.tag));
315
316     TAG* t = swf_InsertTag(prevTag, tag->internals.tag->id);
317     swf_SetBlock(t, tag->internals.tag->data, tag->internals.tag->len);
318     
319     if(swf_isDefiningTag(t)) {
320         int newid = tagmap_add(tagmap, self);
321         swf_SetDefineID(t, newid);
322     }
323
324     int num = swf_GetNumUsedIDs(t);
325     if(num) { // tag has dependencies
326         int * positions = malloc(num*sizeof(int));
327         swf_GetUsedIDs(t, positions);
328         int i;
329         for(i=0;i<num;i++) {
330             int id = GET16(&t->data[positions[i]]);
331             PyObject* obj =  tagmap_id2obj(tag->internals.tagmap, id);
332             if(obj==NULL) {
333                 PyErr_SetString(PyExc_Exception, setError("Internal error: id %d not known in taglist:"));
334                 free(positions);
335                 return 0;
336             }
337             //int newid = tagmap_obj2id(tag->internals.tagmap, obj);
338             int newid = tagmap_obj2id(tagmap, obj);
339             if(newid>=0) {
340                 mylog(" %08x(%d) tag_getTAG: dependency %d) %d->%08x -> assigning(%08x) id %d", (int)self, self->ob_refcnt, i, id, obj, tagmap, newid);
341             } else {
342                 /* TODO: this is only needed for sprites, so maybe it should throw an
343                    exception otherwise */
344                 newid = tagmap_add(tagmap, obj);
345                 mylog(" %08x(%d) tag_getTAG: added dependency %d) %d->%08x -> assigning(%08x) id %d", (int)self, self->ob_refcnt, i, id, obj, tagmap, newid);
346             }
347             PUT16(&t->data[positions[i]], newid);
348         }
349         free(positions);
350     }
351     return t;
352 }
353 //----------------------------------------------------------------------------
354 tag_internals_t* tag_getinternals(PyObject*self)
355 {
356     TagObject*tag = (TagObject*)self;
357     mylog(" %08x(%d) tag_getInternals\n", (int)self, self->ob_refcnt);
358     return &tag->internals;
359 }
360 //----------------------------------------------------------------------------
361 PyObject* tag_getDependencies(PyObject*self)
362 {
363     TagObject*tag = (TagObject*)self;
364     mylog(" %08x(%d) tag_getDependencies\n", (int)self, self->ob_refcnt);
365     return tagmap_getObjectList(tag->internals.tagmap);
366 }
367 //----------------------------------------------------------------------------
368 int tag_print(PyObject * self, FILE * fi, int flags)
369 {
370     TagObject*tag = (TagObject*)self;
371     mylog(" %08x(%d) tag_print flags=%08x\n", (int)self, self->ob_refcnt, flags);
372     if(!fillTAG(self))
373         return -1;
374     //fprintf(fi, "tag-%08x-%d-%s", (int)tag->internals.tag, tag->internals.tag->id, swf_TagGetName(tag->internals.tag));
375     fprintf(fi, "%s", swf_TagGetName(tag->internals.tag));
376     return 0;
377 }
378 //----------------------------------------------------------------------------
379 PyTypeObject TagClass = 
380 {
381     PyObject_HEAD_INIT(NULL)
382     0,
383     tp_name: "Tag",
384     tp_basicsize: sizeof(TagObject),
385     tp_itemsize: 0,
386     tp_dealloc: tag_dealloc,
387     tp_print: tag_print,
388     tp_getattr: tag_getattr,
389     tp_setattr: tag_setattr,
390 };