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