doxygen: prevent GPL license block from being parsed as doxygen comment.
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL 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.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * Contributor(s): Joseph Eagar
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27  
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stddef.h>
31 #include <string.h>
32
33 #include "BKE_idprop.h"
34 #include "BKE_library.h"
35
36 #include "BLI_blenlib.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #define BSTR_EQ(a, b)   (*(a) == *(b) && !strcmp(a, b))
41
42 /* IDPropertyTemplate is a union in DNA_ID.h */
43
44 /*local size table.*/
45 static char idp_size_table[] = {
46         1, /*strings*/
47         sizeof(int),
48         sizeof(float),
49         sizeof(float)*3, /*Vector type, deprecated*/
50         sizeof(float)*16, /*Matrix type, deprecated*/
51         0, /*arrays don't have a fixed size*/
52         sizeof(ListBase), /*Group type*/
53         sizeof(void*),
54         sizeof(double)
55 };
56
57 /* ------------Property Array Type ----------- */
58 #define GETPROP(prop, i) (((IDProperty*)(prop)->data.pointer)+(i))
59
60 /* --------- property array type -------------*/
61
62 /*note: as a start to move away from the stupid IDP_New function, this type
63   has it's own allocation function.*/
64 IDProperty *IDP_NewIDPArray(const char *name)
65 {
66         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
67         prop->type = IDP_IDPARRAY;
68         prop->len = 0;
69         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
70         
71         return prop;
72 }
73
74 IDProperty *IDP_CopyIDPArray(IDProperty *array)
75 {
76         IDProperty *narray = MEM_dupallocN(array), *tmp;
77         int i;
78         
79         narray->data.pointer = MEM_dupallocN(array->data.pointer);
80         for (i=0; i<narray->len; i++) {
81                 /*ok, the copy functions always allocate a new structure,
82                   which doesn't work here.  instead, simply copy the
83                   contents of the new structure into the array cell,
84                   then free it.  this makes for more maintainable
85                   code than simply reimplementing the copy functions
86                   in this loop.*/
87                 tmp = IDP_CopyProperty(GETPROP(narray, i));
88                 memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
89                 MEM_freeN(tmp);
90         }
91         
92         return narray;
93 }
94
95 void IDP_FreeIDPArray(IDProperty *prop)
96 {
97         int i;
98         
99         for (i=0; i<prop->len; i++)
100                 IDP_FreeProperty(GETPROP(prop, i));
101
102         if(prop->data.pointer)
103                 MEM_freeN(prop->data.pointer);
104 }
105
106 /*shallow copies item*/
107 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
108 {
109         IDProperty *old = GETPROP(prop, index);
110         if (index >= prop->len || index < 0) return;
111         if (item != old) IDP_FreeProperty(old);
112         
113         memcpy(GETPROP(prop, index), item, sizeof(IDProperty));
114 }
115
116 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
117 {
118         return GETPROP(prop, index);
119 }
120
121 IDProperty *IDP_AppendArray(IDProperty *prop, IDProperty *item)
122 {
123         IDP_ResizeIDPArray(prop, prop->len+1);
124         IDP_SetIndexArray(prop, prop->len-1, item);
125         return item;
126 }
127
128 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
129 {
130         void *newarr;
131         int newsize=newlen;
132
133         /*first check if the array buffer size has room*/
134         /*if newlen is 200 chars less then totallen, reallocate anyway*/
135         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
136                 int i;
137
138                 for(i=newlen; i<prop->len; i++)
139                         IDP_FreeProperty(GETPROP(prop, i));
140
141                 prop->len = newlen;
142                 return;
143         }
144
145         /* - Note: This code comes from python, here's the corrusponding comment. - */
146         /* This over-allocates proportional to the list size, making room
147          * for additional growth.  The over-allocation is mild, but is
148          * enough to give linear-time amortized behavior over a long
149          * sequence of appends() in the presence of a poorly-performing
150          * system realloc().
151          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
152          */
153         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
154
155         newarr = MEM_callocN(sizeof(IDProperty)*newsize, "idproperty array resized");
156         if (newlen >= prop->len) {
157                 /* newlen is bigger*/
158                 memcpy(newarr, prop->data.pointer, prop->len*sizeof(IDProperty));
159         }
160         else {
161                 int i;
162                 /* newlen is smaller*/
163                 for (i=newlen; i<prop->len; i++) {
164                         IDP_FreeProperty(GETPROP(prop, i));
165                 }
166                 memcpy(newarr, prop->data.pointer, newlen*sizeof(IDProperty));
167         }
168
169         if(prop->data.pointer)
170                 MEM_freeN(prop->data.pointer);
171         prop->data.pointer = newarr;
172         prop->len = newlen;
173         prop->totallen = newsize;
174 }
175
176 /* ----------- Numerical Array Type ----------- */
177 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
178 {
179         if(prop->subtype != IDP_GROUP)
180                 return;
181
182         if(newlen >= prop->len) {
183                 /* bigger */
184                 IDProperty **array= newarr;
185                 IDPropertyTemplate val;
186                 int a;
187
188                 for(a=prop->len; a<newlen; a++) {
189                         val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
190                         array[a]= IDP_New(IDP_GROUP, val, "IDP_ResizeArray group");
191                 }
192         }
193         else {
194                 /* smaller */
195                 IDProperty **array= prop->data.pointer;
196                 int a;
197
198                 for(a=newlen; a<prop->len; a++) {
199                         IDP_FreeProperty(array[a]);
200                         MEM_freeN(array[a]);
201                 }
202         }
203 }
204
205 /*this function works for strings too!*/
206 void IDP_ResizeArray(IDProperty *prop, int newlen)
207 {
208         void *newarr;
209         int newsize=newlen;
210
211         /*first check if the array buffer size has room*/
212         /*if newlen is 200 chars less then totallen, reallocate anyway*/
213         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
214                 idp_resize_group_array(prop, newlen, prop->data.pointer);
215                 prop->len = newlen;
216                 return;
217         }
218
219         /* - Note: This code comes from python, here's the corrusponding comment. - */
220         /* This over-allocates proportional to the list size, making room
221          * for additional growth.  The over-allocation is mild, but is
222          * enough to give linear-time amortized behavior over a long
223          * sequence of appends() in the presence of a poorly-performing
224          * system realloc().
225          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
226          */
227         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
228
229         newarr = MEM_callocN(idp_size_table[(int)prop->subtype]*newsize, "idproperty array resized");
230         if (newlen >= prop->len) {
231                 /* newlen is bigger*/
232                 memcpy(newarr, prop->data.pointer, prop->len*idp_size_table[(int)prop->subtype]);
233                 idp_resize_group_array(prop, newlen, newarr);
234         }
235         else {
236                 /* newlen is smaller*/
237                 idp_resize_group_array(prop, newlen, newarr);
238                 memcpy(newarr, prop->data.pointer, newlen*prop->len*idp_size_table[(int)prop->subtype]);
239         }
240
241         MEM_freeN(prop->data.pointer);
242         prop->data.pointer = newarr;
243         prop->len = newlen;
244         prop->totallen = newsize;
245 }
246
247 void IDP_FreeArray(IDProperty *prop)
248 {
249         if (prop->data.pointer) {
250                 idp_resize_group_array(prop, 0, NULL);
251                 MEM_freeN(prop->data.pointer);
252         }
253 }
254
255
256  static IDProperty *idp_generic_copy(IDProperty *prop)
257  {
258         IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
259
260         BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
261         newp->type = prop->type;
262         newp->flag = prop->flag;
263         newp->data.val = prop->data.val;
264         newp->data.val2 = prop->data.val2;
265
266         return newp;
267  }
268
269 static IDProperty *IDP_CopyArray(IDProperty *prop)
270 {
271         IDProperty *newp = idp_generic_copy(prop);
272
273         if (prop->data.pointer) {
274                 newp->data.pointer = MEM_dupallocN(prop->data.pointer);
275
276                 if(prop->type == IDP_GROUP) {
277                         IDProperty **array= newp->data.pointer;
278                         int a;
279
280                         for(a=0; a<prop->len; a++)
281                                 array[a]= IDP_CopyProperty(array[a]);
282                 }
283         }
284         newp->len = prop->len;
285         newp->subtype = prop->subtype;
286         newp->totallen = prop->totallen;
287
288         return newp;
289 }
290
291 /*taken from readfile.c*/
292 #define SWITCH_LONGINT(a) { \
293         char s_i, *p_i; \
294         p_i= (char *)&(a);  \
295         s_i=p_i[0]; p_i[0]=p_i[7]; p_i[7]=s_i; \
296         s_i=p_i[1]; p_i[1]=p_i[6]; p_i[6]=s_i; \
297         s_i=p_i[2]; p_i[2]=p_i[5]; p_i[5]=s_i; \
298         s_i=p_i[3]; p_i[3]=p_i[4]; p_i[4]=s_i; }
299
300
301
302 /* ---------- String Type ------------ */
303 IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
304 {
305         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
306
307         if (st == NULL) {
308                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
309                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
310                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
311         }
312         else {
313                 int stlen = strlen(st);
314
315                 if(maxlen > 0 && maxlen < stlen)
316                         stlen = maxlen;
317
318                 stlen++; /* null terminator '\0' */
319
320                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
321                 prop->len = prop->totallen = stlen;
322                 BLI_strncpy(prop->data.pointer, st, stlen);
323         }
324
325         prop->type = IDP_STRING;
326         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
327
328         return prop;
329 }
330
331 static IDProperty *IDP_CopyString(IDProperty *prop)
332 {
333         IDProperty *newp = idp_generic_copy(prop);
334
335         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
336         newp->len = prop->len;
337         newp->subtype = prop->subtype;
338         newp->totallen = prop->totallen;
339
340         return newp;
341 }
342
343
344 void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
345 {
346         int stlen;
347
348         stlen = strlen(st);
349
350         if(maxlen > 0 && maxlen < stlen)
351                 stlen= maxlen;
352
353         stlen++; /* make room for null byte */
354
355         IDP_ResizeArray(prop, stlen);
356         BLI_strncpy(prop->data.pointer, st, stlen);
357 }
358
359 void IDP_ConcatStringC(IDProperty *prop, const char *st)
360 {
361         int newlen;
362
363         newlen = prop->len + strlen(st);
364         /*we have to remember that prop->len includes the null byte for strings.
365          so there's no need to add +1 to the resize function.*/
366         IDP_ResizeArray(prop, newlen);
367         strcat(prop->data.pointer, st);
368 }
369
370 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
371 {
372         int newlen;
373
374         /*since ->len for strings includes the NULL byte, we have to subtract one or
375          we'll get an extra null byte after each concatination operation.*/
376         newlen = str1->len + append->len - 1;
377         IDP_ResizeArray(str1, newlen);
378         strcat(str1->data.pointer, append->data.pointer);
379 }
380
381 void IDP_FreeString(IDProperty *prop)
382 {
383         if(prop->data.pointer)
384                 MEM_freeN(prop->data.pointer);
385 }
386
387
388 /*-------- ID Type, not in use yet -------*/
389
390 void IDP_LinkID(IDProperty *prop, ID *id)
391 {
392         if (prop->data.pointer) ((ID*)prop->data.pointer)->us--;
393         prop->data.pointer = id;
394         id_us_plus(id);
395 }
396
397 void IDP_UnlinkID(IDProperty *prop)
398 {
399         ((ID*)prop->data.pointer)->us--;
400 }
401
402 /*-------- Group Functions -------*/
403
404 /*checks if a property with the same name as prop exists, and if so replaces it.*/
405 static IDProperty *IDP_CopyGroup(IDProperty *prop)
406 {
407         IDProperty *newp = idp_generic_copy(prop), *link;
408         newp->len = prop->len;
409         
410         for (link=prop->data.group.first; link; link=link->next) {
411                 BLI_addtail(&newp->data.group, IDP_CopyProperty(link));
412         }
413
414         return newp;
415 }
416
417 /* use for syncing proxies.
418  * When values name and types match, copy the values, else ignore */
419 void IDP_SyncGroupValues(IDProperty *dest, IDProperty *src)
420 {
421         IDProperty *loop, *prop;
422         for (prop=src->data.group.first; prop; prop=prop->next) {
423                 for (loop=dest->data.group.first; loop; loop=loop->next) {
424                         if (strcmp(loop->name, prop->name)==0) {
425                                 if(prop->type==loop->type) {
426
427                                         switch (prop->type) {
428                                                 case IDP_INT:
429                                                 case IDP_FLOAT:
430                                                 case IDP_DOUBLE:
431                                                         loop->data= prop->data;
432                                                         break;
433                                                 case IDP_GROUP:
434                                                         IDP_SyncGroupValues(loop, prop);
435                                                         break;
436                                                 default:
437                                                 {
438                                                         IDProperty *tmp= loop;
439                                                         IDProperty *copy= IDP_CopyProperty(prop);
440
441                                                         BLI_insertlinkafter(&dest->data.group, loop, copy);
442                                                         BLI_remlink(&dest->data.group, tmp);
443
444                                                         IDP_FreeProperty(tmp);
445                                                         MEM_freeN(tmp);
446                                                 }
447                                         }
448                                 }
449                                 break;
450                         }
451                 }
452         }
453 }
454
455 /*
456  replaces all properties with the same name in a destination group from a source group.
457 */
458 void IDP_ReplaceGroupInGroup(IDProperty *dest, IDProperty *src)
459 {
460         IDProperty *loop, *prop;
461         for (prop=src->data.group.first; prop; prop=prop->next) {
462                 for (loop=dest->data.group.first; loop; loop=loop->next) {
463                         if (BSTR_EQ(loop->name, prop->name)) {
464                                 IDProperty *copy = IDP_CopyProperty(prop);
465
466                                 BLI_insertlink(&dest->data.group, loop, copy);
467
468                                 BLI_remlink(&dest->data.group, loop);
469                                 IDP_FreeProperty(loop);
470                                 MEM_freeN(loop);
471                                 break;
472                         }
473                 }
474
475                 /* only add at end if not added yet */
476                 if (loop == NULL) {
477                         IDProperty *copy = IDP_CopyProperty(prop);
478                         dest->len++;
479                         BLI_addtail(&dest->data.group, copy);
480                 }
481         }
482 }
483 /*
484  replaces a property with the same name in a group, or adds 
485  it if the propery doesn't exist.
486 */
487 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
488 {
489         IDProperty *loop;
490         if((loop= IDP_GetPropertyFromGroup(group, prop->name)))  {
491                 BLI_insertlink(&group->data.group, loop, prop);
492                 
493                 BLI_remlink(&group->data.group, loop);
494                 IDP_FreeProperty(loop);
495                 MEM_freeN(loop);                        
496         }
497         else {
498                 group->len++;
499                 BLI_addtail(&group->data.group, prop);
500         }
501 }
502
503 /*returns 0 if an id property with the same name exists and it failed,
504   or 1 if it succeeded in adding to the group.*/
505 int IDP_AddToGroup(IDProperty *group, IDProperty *prop)
506 {
507         if(IDP_GetPropertyFromGroup(group, prop->name) == NULL)  {
508                 group->len++;
509                 BLI_addtail(&group->data.group, prop);
510                 return 1;
511         }
512
513         return 0;
514 }
515
516 int IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
517 {
518         if(IDP_GetPropertyFromGroup(group, pnew->name) == NULL)  {
519                 group->len++;
520                 BLI_insertlink(&group->data.group, previous, pnew);
521                 return 1;
522         }
523
524         return 0;
525 }
526
527 void IDP_RemFromGroup(IDProperty *group, IDProperty *prop)
528 {
529         group->len--;
530         BLI_remlink(&group->data.group, prop);
531 }
532
533 IDProperty *IDP_GetPropertyFromGroup(IDProperty *prop, const char *name)
534 {
535         return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
536 }
537
538 IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, const char type)
539 {
540         IDProperty *idprop= IDP_GetPropertyFromGroup(prop, name);
541         return (idprop && idprop->type == type) ? idprop : NULL;
542 }
543
544 typedef struct IDPIter {
545         void *next;
546         IDProperty *parent;
547 } IDPIter;
548
549 void *IDP_GetGroupIterator(IDProperty *prop)
550 {
551         IDPIter *iter = MEM_callocN(sizeof(IDPIter), "IDPIter");
552         iter->next = prop->data.group.first;
553         iter->parent = prop;
554         return (void*) iter;
555 }
556
557 IDProperty *IDP_GroupIterNext(void *vself)
558 {
559         IDPIter *self = (IDPIter*) vself;
560         Link *next = (Link*) self->next;
561         if (self->next == NULL) {
562                 MEM_freeN(self);
563                 return NULL;
564         }
565
566         self->next = next->next;
567         return (void*) next;
568 }
569
570 void IDP_FreeIterBeforeEnd(void *vself)
571 {
572         MEM_freeN(vself);
573 }
574
575 /*Ok, the way things work, Groups free the ID Property structs of their children.
576   This is because all ID Property freeing functions free only direct data (not the ID Property
577   struct itself), but for Groups the child properties *are* considered
578   direct data.*/
579 static void IDP_FreeGroup(IDProperty *prop)
580 {
581         IDProperty *loop;
582         for (loop=prop->data.group.first; loop; loop=loop->next)
583         {
584                 IDP_FreeProperty(loop);
585         }
586         BLI_freelistN(&prop->data.group);
587 }
588
589
590 /*-------- Main Functions --------*/
591 IDProperty *IDP_CopyProperty(IDProperty *prop)
592 {
593         switch (prop->type) {
594                 case IDP_GROUP: return IDP_CopyGroup(prop);
595                 case IDP_STRING: return IDP_CopyString(prop);
596                 case IDP_ARRAY: return IDP_CopyArray(prop);
597                 case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
598                 default: return idp_generic_copy(prop);
599         }
600 }
601
602 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
603 {
604         if (id->properties) return id->properties;
605         else {
606                 if (create_if_needed) {
607                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
608                         id->properties->type = IDP_GROUP;
609                         /* dont overwite the data's name and type
610                          * some functions might need this if they
611                          * dont have a real ID, should be named elsewhere - Campbell */
612                         /* strcpy(id->name, "top_level_group");*/
613                 }
614                 return id->properties;
615         }
616 }
617
618 int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
619 {
620         if(prop1 == NULL && prop2 == NULL)
621                 return 1;
622         else if(prop1 == NULL || prop2 == NULL)
623                 return 0;
624         else if(prop1->type != prop2->type)
625                 return 0;
626
627         if(prop1->type == IDP_INT)
628                 return (IDP_Int(prop1) == IDP_Int(prop2));
629         else if(prop1->type == IDP_FLOAT)
630                 return (IDP_Float(prop1) == IDP_Float(prop2));
631         else if(prop1->type == IDP_DOUBLE)
632                 return (IDP_Double(prop1) == IDP_Double(prop2));
633         else if(prop1->type == IDP_STRING)
634                 return BSTR_EQ(IDP_String(prop1), IDP_String(prop2));
635         else if(prop1->type == IDP_ARRAY) {
636                 if(prop1->len == prop2->len && prop1->subtype == prop2->subtype)
637                         return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype]*prop1->len);
638                 else
639                         return 0;
640         }
641         else if(prop1->type == IDP_GROUP) {
642                 IDProperty *link1, *link2;
643
644                 if(BLI_countlist(&prop1->data.group) != BLI_countlist(&prop2->data.group))
645                         return 0;
646
647                 for(link1=prop1->data.group.first; link1; link1=link1->next) {
648                         link2= IDP_GetPropertyFromGroup(prop2, link1->name);
649
650                         if(!IDP_EqualsProperties(link1, link2))
651                                 return 0;
652                 }
653
654                 return 1;
655         }
656         else if(prop1->type == IDP_IDPARRAY) {
657                 IDProperty *array1= IDP_IDPArray(prop1);
658                 IDProperty *array2= IDP_IDPArray(prop2);
659                 int i;
660
661                 if(prop1->len != prop2->len)
662                         return 0;
663                 
664                 for(i=0; i<prop1->len; i++)
665                         if(!IDP_EqualsProperties(&array1[i], &array2[i]))
666                                 return 0;
667         }
668         
669         return 1;
670 }
671
672 IDProperty *IDP_New(int type, IDPropertyTemplate val, const char *name)
673 {
674         IDProperty *prop=NULL;
675
676         switch (type) {
677                 case IDP_INT:
678                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
679                         prop->data.val = val.i;
680                         break;
681                 case IDP_FLOAT:
682                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
683                         *(float*)&prop->data.val = val.f;
684                         break;
685                 case IDP_DOUBLE:
686                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
687                         *(double*)&prop->data.val = val.d;
688                         break;          
689                 case IDP_ARRAY:
690                 {
691                         /*for now, we only support float and int and double arrays*/
692                         if (val.array.type == IDP_FLOAT || val.array.type == IDP_INT || val.array.type == IDP_DOUBLE || val.array.type == IDP_GROUP) {
693                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
694                                 prop->subtype = val.array.type;
695                                 if (val.array.len)
696                                         prop->data.pointer = MEM_callocN(idp_size_table[val.array.type]*val.array.len, "id property array");
697                                 prop->len = prop->totallen = val.array.len;
698                                 break;
699                         } else {
700                                 return NULL;
701                         }
702                 }
703                 case IDP_STRING:
704                 {
705                         char *st = val.str;
706
707                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
708                         if (st == NULL) {
709                                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
710                                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
711                                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
712                         } else {
713                                 int stlen = strlen(st) + 1;
714                                 prop->data.pointer = MEM_mallocN(stlen, "id property string 2");
715                                 prop->len = prop->totallen = stlen;
716                                 memcpy(prop->data.pointer, st, stlen);
717                         }
718                         break;
719                 }
720                 case IDP_GROUP:
721                 {
722                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
723                         /* heh I think all needed values are set properly by calloc anyway :) */
724                         break;
725                 }
726                 default:
727                 {
728                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
729                         break;
730                 }
731         }
732
733         prop->type = type;
734         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
735         
736         return prop;
737 }
738
739 /*NOTE: this will free all child properties including list arrays and groups!
740   Also, note that this does NOT unlink anything!  Plus it doesn't free
741   the actual IDProperty struct either.*/
742 void IDP_FreeProperty(IDProperty *prop)
743 {
744         switch (prop->type) {
745                 case IDP_ARRAY:
746                         IDP_FreeArray(prop);
747                         break;
748                 case IDP_STRING:
749                         IDP_FreeString(prop);
750                         break;
751                 case IDP_GROUP:
752                         IDP_FreeGroup(prop);
753                         break;
754                 case IDP_IDPARRAY:
755                         IDP_FreeIDPArray(prop);
756                         break;
757         }
758 }
759
760 /*Unlinks any IDProperty<->ID linkage that might be going on.
761   note: currently unused.*/
762 void IDP_UnlinkProperty(IDProperty *prop)
763 {
764         switch (prop->type) {
765                 case IDP_ID:
766                         IDP_UnlinkID(prop);
767         }
768 }