IDP_CopyGroup wasn't copying the length, thanks Caedes for finding this one
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /**
2  * $Id: idprop.c
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
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 Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * Contributor(s): Joseph Eagar
27  *
28  * ***** END GPL/BL DUAL LICENSE BLOCK *****
29  */
30  
31 #include "DNA_listBase.h"
32 #include "DNA_ID.h"
33
34 #include "BKE_idprop.h"
35 #include "BKE_global.h"
36 #include "BKE_library.h"
37 #include "BKE_utildefines.h"
38
39 #include "BLI_blenlib.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #define BSTR_EQ(a, b)   (*(a) == *(b) && !strcmp(a, b))
48
49 /* IDPropertyTemplate is a union in DNA_ID.h */
50
51 /*local size table.*/
52 static char idp_size_table[] = {
53         1, /*strings*/
54         sizeof(int),
55         sizeof(float),
56         sizeof(float)*3, /*Vector type, deprecated*/
57         sizeof(float)*16, /*Matrix type, deprecated*/
58         0, /*arrays don't have a fixed size*/
59         sizeof(ListBase), /*Group type*/
60         sizeof(void*)
61 };
62
63
64 /* ----------- Array Type ----------- */
65
66 /*this function works for strings too!*/
67 void IDP_ResizeArray(IDProperty *prop, int newlen)
68 {
69         void *newarr;
70         int newsize=newlen;
71
72         /*first check if the array buffer size has room*/
73         /*if newlen is 200 chars less then totallen, reallocate anyway*/
74         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
75                 prop->len = newlen;
76                 return;
77         }
78
79         /* - Note: This code comes from python, here's the corrusponding comment. - */
80         /* This over-allocates proportional to the list size, making room
81          * for additional growth.  The over-allocation is mild, but is
82          * enough to give linear-time amortized behavior over a long
83          * sequence of appends() in the presence of a poorly-performing
84          * system realloc().
85          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
86          */
87         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
88
89         newarr = MEM_callocN(idp_size_table[prop->type]*newsize, "idproperty array resized");
90         /*newlen is bigger*/
91         if (newlen >= prop->len) memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[prop->type]);
92         /*newlen is smaller*/
93         else memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[prop->type]);
94
95         MEM_freeN(prop->data.pointer);
96         prop->data.pointer = newarr;
97         prop->len = newlen;
98         prop->totallen = newsize;
99 }
100
101  void IDP_FreeArray(IDProperty *prop)
102 {
103         if (prop->data.pointer)
104                 MEM_freeN(prop->data.pointer);
105 }
106
107
108  static IDProperty *idp_generic_copy(IDProperty *prop)
109  {
110         IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
111
112         strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
113         newp->type = prop->type;
114         newp->flag = prop->flag;
115         newp->data.val = prop->data.val;
116
117         return newp;
118  }
119
120 IDProperty *IDP_CopyArray(IDProperty *prop)
121 {
122         IDProperty *newp = idp_generic_copy(prop);
123
124         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
125         newp->len = prop->len;
126         newp->subtype = prop->subtype;
127         newp->totallen = prop->totallen;
128
129         return newp;
130 }
131
132 /*taken from readfile.c*/
133 #define SWITCH_LONGINT(a) { \
134     char s_i, *p_i; \
135     p_i= (char *)&(a);  \
136     s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
137     s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
138     s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
139     s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
140
141
142
143 /* ---------- String Type ------------ */
144 IDProperty *IDP_CopyString(IDProperty *prop)
145 {
146         IDProperty *newp = idp_generic_copy(prop);
147
148         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
149         newp->len = prop->len;
150         newp->subtype = prop->subtype;
151         newp->totallen = prop->totallen;
152
153         return newp;
154 }
155
156
157 void IDP_AssignString(IDProperty *prop, char *st)
158 {
159         int stlen;
160
161         stlen = strlen(st);
162
163         IDP_ResizeArray(prop, stlen+1); /*make room for null byte :) */
164         strcpy(prop->data.pointer, st);
165 }
166
167 void IDP_ConcatStringC(IDProperty *prop, char *st)
168 {
169         int newlen;
170
171         newlen = prop->len + strlen(st);
172         /*we have to remember that prop->len includes the null byte for strings.
173          so there's no need to add +1 to the resize function.*/
174         IDP_ResizeArray(prop, newlen);
175         strcat(prop->data.pointer, st);
176 }
177
178 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
179 {
180         int newlen;
181
182         /*since ->len for strings includes the NULL byte, we have to subtract one or
183          we'll get an extra null byte after each concatination operation.*/
184         newlen = str1->len + append->len - 1;
185         IDP_ResizeArray(str1, newlen);
186         strcat(str1->data.pointer, append->data.pointer);
187 }
188
189 void IDP_FreeString(IDProperty *prop)
190 {
191         MEM_freeN(prop->data.pointer);
192 }
193
194
195 /*-------- ID Type, not in use yet -------*/
196
197 void IDP_LinkID(IDProperty *prop, ID *id)
198 {
199         if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
200         prop->data.pointer = id;
201         id_us_plus(id);
202 }
203
204 void IDP_UnlinkID(IDProperty *prop)
205 {
206         ((ID*)prop->data.pointer)->us--;
207 }
208
209 /*-------- Group Functions -------*/
210
211 /*checks if a property with the same name as prop exists, and if so replaces it.*/
212 IDProperty *IDP_CopyGroup(IDProperty *prop)
213 {
214         IDProperty *newp = idp_generic_copy(prop), *link;
215         newp->len = prop->len;
216         
217         for (link=prop->data.group.first; link; link=link->next) {
218                 BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
219         }
220
221         return newp;
222 }
223
224 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
225 {
226         IDProperty *loop;
227         for (loop=group->data.group.first; loop; loop=loop->next) {
228                 if (BSTR_EQ(loop->name, prop->name)) {
229                         if (loop->next) BLI_insertlinkbefore(&group->data.group, loop->next, prop);
230                         else BLI_addtail(&group->data.group, prop);
231                         BLI_remlink(&group->data.group, loop);
232                         IDP_FreeProperty(loop);
233                         MEM_freeN(loop);
234                         group->len++;
235                         return;
236                 }
237         }
238
239         group->len++;
240         BLI_addtail(&group->data.group, prop);
241 }
242
243 /*returns 0 if an id property with the same name exists and it failed,
244   or 1 if it succeeded in adding to the group.*/
245 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
246 {
247         IDProperty *loop;
248         for (loop=group->data.group.first; loop; loop=loop->next) {
249                 if (BSTR_EQ(loop->name, prop->name)) return 0;
250         }
251
252         group->len++;
253         BLI_addtail(&group->data.group, prop);
254
255         return 1;
256 }
257
258 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
259 {
260         IDProperty *loop;
261         for (loop=group->data.group.first; loop; loop=loop->next) {
262                 if (BSTR_EQ(loop->name, pnew->name)) return 0;
263         }
264         
265         group->len++;
266         
267         BLI_insertlink(&group->data.group, previous, pnew);
268         return 1;
269 }
270
271 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
272 {
273         group->len--;
274         BLI_remlink(&group->data.group, prop);
275 }
276
277 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, char *name)
278 {
279         IDProperty *loop;
280         for (loop=prop->data.group.first; loop; loop=loop->next) {
281                 if (strcmp(loop->name, name)==0) return loop;
282         }
283         return NULL;
284 }
285
286 typedef struct IDPIter {
287         void *next;
288         IDProperty *parent;
289 } IDPIter;
290
291 void *IDP_GetGroupIterator(IDProperty *prop)
292 {
293         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
294         iter->next = prop->data.group.first;
295         iter->parent = prop;
296         return (void*) iter;
297 }
298
299 IDProperty *IDP_GroupIterNext(void *vself)
300 {
301         IDPIter *self = (IDPIter*) vself;
302         Link *next = (Link*) self->next;
303         if (self->next == NULL) {
304                 MEM_freeN(self);
305                 return NULL;
306         }
307
308         self->next = next->next;
309         return (void*) next;
310 }
311
312 void IDP_FreeIterBeforeEnd(void *vself)
313 {
314         MEM_freeN(vself);
315 }
316
317 /*Ok, the way things work, Groups free the ID Property structs of their children.
318   This is because all ID Property freeing functions free only direct data (not the ID Property
319   struct itself), but for Groups the child properties *are* considered
320   direct data.*/
321 void IDP_FreeGroup(IDProperty *prop)
322 {
323         IDProperty *loop, *next;
324         for (loop=prop->data.group.first; loop; loop=next)
325         {
326                 next = loop->next;
327                 BLI_remlink(&prop->data.group, loop);
328                 IDP_FreeProperty(loop);
329                 MEM_freeN(loop);
330         }
331 }
332
333
334 /*-------- Main Functions --------*/
335 IDProperty *IDP_CopyProperty(IDProperty *prop)
336 {
337         switch (prop->type) {
338                 case IDP_GROUP: return IDP_CopyGroup(prop);
339                 case IDP_STRING: return IDP_CopyString(prop);
340                 case IDP_ARRAY: return IDP_CopyArray(prop);
341                 default: return idp_generic_copy(prop);
342         }
343 }
344
345 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
346 {
347         if (id->properties) return id->properties;
348         else {
349                 if (create_if_needed) {
350                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
351                         id->properties->type = IDP_GROUP;
352                 }
353                 return id->properties;
354         }
355 }
356
357 IDProperty *IDP_New(int type, IDPropertyTemplate val, char *name)
358 {
359         IDProperty *prop=NULL;
360
361         switch (type) {
362                 case IDP_INT:
363                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
364                         prop->data.val = val.i;
365                         break;
366                 case IDP_FLOAT:
367                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
368                         *(float*)&prop->data.val = val.f;
369                         break;
370                 case IDP_ARRAY:
371                 {
372                         /*for now, we only support float and int arrays*/
373                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT) {
374                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
375                                 prop->len = prop->totallen = val.array.len;
376                                 prop->subtype = val.array.type;
377                                 prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
378                                 break;
379                         } else {
380                                 return NULL;
381                         }
382                 }
383                 case IDP_STRING:
384                 {
385                         char *st = val.str;
386                         int stlen;
387
388                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
389                         if (st == NULL) {
390                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
391                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
392                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
393                         } else {
394                                 stlen = strlen(st) + 1;
395                                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
396                                 prop->len = prop->totallen = stlen;
397                                 strcpy(prop->data.pointer, st);
398                         }
399                         break;
400                 }
401                 case IDP_GROUP:
402                 {
403                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
404                         /* heh I think all needed values are set properly by calloc anyway :) */
405                         break;
406                 }
407                 default:
408                 {
409                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
410                         break;
411                 }
412         }
413
414         prop->type = type;
415         strncpy(prop->name, name, MAX_IDPROP_NAME);
416         return prop;
417 }
418
419 /*NOTE: this will free all child properties of list arrays and groups!
420   Also, note that this does NOT unlink anything!  Plus it doesn't free
421   the actual IDProperty struct either.*/
422 void IDP_FreeProperty(IDProperty *prop)
423 {
424         switch (prop->type) {
425                 case IDP_ARRAY:
426                         IDP_FreeArray(prop);
427                         break;
428                 case IDP_STRING:
429                         IDP_FreeString(prop);
430                         break;
431                 case IDP_GROUP:
432                         IDP_FreeGroup(prop);
433                         break;
434         }
435 }
436
437 /*Unlinks any IDProperty<->ID linkage that might be going on.*/
438 void IDP_UnlinkProperty(IDProperty *prop)
439 {
440         switch (prop->type) {
441                 case IDP_ID:
442                         IDP_UnlinkID(prop);
443         }
444 }