42c7869a365ad285f1642f71c1efeee7a9ae1613
[blender.git] / source / blender / blenkernel / intern / idprop.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Joseph Eagar
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/blenkernel/intern/idprop.c
27  *  \ingroup bke
28  */
29
30  
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stddef.h>
34 #include <string.h>
35
36 #include "BKE_idprop.h"
37 #include "BKE_library.h"
38
39 #include "BLI_blenlib.h"
40
41 #include "MEM_guardedalloc.h"
42
43 /* IDPropertyTemplate is a union in DNA_ID.h */
44
45 /*local size table.*/
46 static char idp_size_table[] = {
47         1, /*strings*/
48         sizeof(int),
49         sizeof(float),
50         sizeof(float) * 3, /*Vector type, deprecated*/
51         sizeof(float) * 16, /*Matrix type, deprecated*/
52         0, /*arrays don't have a fixed size*/
53         sizeof(ListBase), /*Group type*/
54         sizeof(void *),
55         sizeof(double)
56 };
57
58 /* ------------Property Array Type ----------- */
59 #define GETPROP(prop, i) (((IDProperty *)(prop)->data.pointer) + (i))
60
61 /* --------- property array type -------------*/
62
63 /* note: as a start to move away from the stupid IDP_New function, this type
64  * has it's own allocation function.*/
65 IDProperty *IDP_NewIDPArray(const char *name)
66 {
67         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
68         prop->type = IDP_IDPARRAY;
69         prop->len = 0;
70         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
71         
72         return prop;
73 }
74
75 IDProperty *IDP_CopyIDPArray(IDProperty *array)
76 {
77         /* don't use MEM_dupallocN because this may be part of an array */
78         IDProperty *narray = MEM_mallocN(sizeof(IDProperty), "IDP_CopyIDPArray"), *tmp;
79         int i;
80
81         *narray = *array;
82
83         narray->data.pointer = MEM_dupallocN(array->data.pointer);
84         for (i = 0; i < narray->len; i++) {
85                 /* ok, the copy functions always allocate a new structure,
86                  * which doesn't work here.  instead, simply copy the
87                  * contents of the new structure into the array cell,
88                  * then free it.  this makes for more maintainable
89                  * code than simply reimplementing the copy functions
90                  * in this loop.*/
91                 tmp = IDP_CopyProperty(GETPROP(narray, i));
92                 memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
93                 MEM_freeN(tmp);
94         }
95         
96         return narray;
97 }
98
99 void IDP_FreeIDPArray(IDProperty *prop)
100 {
101         int i;
102         
103         for (i = 0; i < prop->len; i++)
104                 IDP_FreeProperty(GETPROP(prop, i));
105
106         if (prop->data.pointer)
107                 MEM_freeN(prop->data.pointer);
108 }
109
110 /*shallow copies item*/
111 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
112 {
113         IDProperty *old = GETPROP(prop, index);
114         if (index >= prop->len || index < 0) return;
115         if (item != old) IDP_FreeProperty(old);
116         
117         memcpy(GETPROP(prop, index), item, sizeof(IDProperty));
118 }
119
120 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
121 {
122         return GETPROP(prop, index);
123 }
124
125 IDProperty *IDP_AppendArray(IDProperty *prop, IDProperty *item)
126 {
127         IDP_ResizeIDPArray(prop, prop->len + 1);
128         IDP_SetIndexArray(prop, prop->len - 1, item);
129         return item;
130 }
131
132 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
133 {
134         void *newarr;
135         int newsize = newlen;
136
137         /*first check if the array buffer size has room*/
138         /*if newlen is 200 chars less then totallen, reallocate anyway*/
139         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
140                 int i;
141
142                 for (i = newlen; i < prop->len; i++)
143                         IDP_FreeProperty(GETPROP(prop, i));
144
145                 prop->len = newlen;
146                 return;
147         }
148
149         /* - Note: This code comes from python, here's the corresponding comment. - */
150         /* This over-allocates proportional to the list size, making room
151          * for additional growth.  The over-allocation is mild, but is
152          * enough to give linear-time amortized behavior over a long
153          * sequence of appends() in the presence of a poorly-performing
154          * system realloc().
155          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
156          */
157         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
158
159         newarr = MEM_callocN(sizeof(IDProperty) * newsize, "idproperty array resized");
160         if (newlen >= prop->len) {
161                 /* newlen is bigger*/
162                 memcpy(newarr, prop->data.pointer, prop->len * sizeof(IDProperty));
163         }
164         else {
165                 int i;
166                 /* newlen is smaller*/
167                 for (i = newlen; i < prop->len; i++) {
168                         IDP_FreeProperty(GETPROP(prop, i));
169                 }
170                 memcpy(newarr, prop->data.pointer, newlen * sizeof(IDProperty));
171         }
172
173         if (prop->data.pointer)
174                 MEM_freeN(prop->data.pointer);
175         prop->data.pointer = newarr;
176         prop->len = newlen;
177         prop->totallen = newsize;
178 }
179
180 /* ----------- Numerical Array Type ----------- */
181 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
182 {
183         if (prop->subtype != IDP_GROUP)
184                 return;
185
186         if (newlen >= prop->len) {
187                 /* bigger */
188                 IDProperty **array = newarr;
189                 IDPropertyTemplate val;
190                 int a;
191
192                 for (a = prop->len; a < newlen; a++) {
193                         val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
194                         array[a] = IDP_New(IDP_GROUP, &val, "IDP_ResizeArray group");
195                 }
196         }
197         else {
198                 /* smaller */
199                 IDProperty **array = prop->data.pointer;
200                 int a;
201
202                 for (a = newlen; a < prop->len; a++) {
203                         IDP_FreeProperty(array[a]);
204                         MEM_freeN(array[a]);
205                 }
206         }
207 }
208
209 /*this function works for strings too!*/
210 void IDP_ResizeArray(IDProperty *prop, int newlen)
211 {
212         void *newarr;
213         int newsize = newlen;
214
215         /*first check if the array buffer size has room*/
216         /*if newlen is 200 chars less then totallen, reallocate anyway*/
217         if (newlen <= prop->totallen && prop->totallen - newlen < 200) {
218                 idp_resize_group_array(prop, newlen, prop->data.pointer);
219                 prop->len = newlen;
220                 return;
221         }
222
223         /* - Note: This code comes from python, here's the corresponding comment. - */
224         /* This over-allocates proportional to the list size, making room
225          * for additional growth.  The over-allocation is mild, but is
226          * enough to give linear-time amortized behavior over a long
227          * sequence of appends() in the presence of a poorly-performing
228          * system realloc().
229          * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
230          */
231         newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
232
233         newarr = MEM_callocN(idp_size_table[(int)prop->subtype] * newsize, "idproperty array resized");
234         if (newlen >= prop->len) {
235                 /* newlen is bigger*/
236                 memcpy(newarr, prop->data.pointer, prop->len * idp_size_table[(int)prop->subtype]);
237                 idp_resize_group_array(prop, newlen, newarr);
238         }
239         else {
240                 /* newlen is smaller*/
241                 idp_resize_group_array(prop, newlen, newarr);
242                 memcpy(newarr, prop->data.pointer, newlen * idp_size_table[(int)prop->subtype]);
243         }
244
245         MEM_freeN(prop->data.pointer);
246         prop->data.pointer = newarr;
247         prop->len = newlen;
248         prop->totallen = newsize;
249 }
250
251 void IDP_FreeArray(IDProperty *prop)
252 {
253         if (prop->data.pointer) {
254                 idp_resize_group_array(prop, 0, NULL);
255                 MEM_freeN(prop->data.pointer);
256         }
257 }
258
259
260 static IDProperty *idp_generic_copy(IDProperty *prop)
261 {
262         IDProperty *newp = MEM_callocN(sizeof(IDProperty), "IDProperty array dup");
263
264         BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
265         newp->type = prop->type;
266         newp->flag = prop->flag;
267         newp->data.val = prop->data.val;
268         newp->data.val2 = prop->data.val2;
269
270         return newp;
271 }
272
273 static IDProperty *IDP_CopyArray(IDProperty *prop)
274 {
275         IDProperty *newp = idp_generic_copy(prop);
276
277         if (prop->data.pointer) {
278                 newp->data.pointer = MEM_dupallocN(prop->data.pointer);
279
280                 if (prop->type == IDP_GROUP) {
281                         IDProperty **array = newp->data.pointer;
282                         int a;
283
284                         for (a = 0; a < prop->len; a++)
285                                 array[a] = IDP_CopyProperty(array[a]);
286                 }
287         }
288         newp->len = prop->len;
289         newp->subtype = prop->subtype;
290         newp->totallen = prop->totallen;
291
292         return newp;
293 }
294
295 /*taken from readfile.c*/
296 #define SWITCH_LONGINT(a) { \
297                 char s_i, *p_i; \
298                 p_i = (char *)& (a);  \
299                 s_i = p_i[0]; p_i[0] = p_i[7]; p_i[7] = s_i; \
300                 s_i = p_i[1]; p_i[1] = p_i[6]; p_i[6] = s_i; \
301                 s_i = p_i[2]; p_i[2] = p_i[5]; p_i[5] = s_i; \
302                 s_i = p_i[3]; p_i[3] = p_i[4]; p_i[4] = s_i; \
303         } (void)0
304
305
306
307 /* ---------- String Type ------------ */
308 IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
309 {
310         IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
311
312         if (st == NULL) {
313                 prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
314                 prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
315                 prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
316         }
317         else {
318                 int stlen = strlen(st);
319
320                 if (maxlen > 0 && maxlen < stlen)
321                         stlen = maxlen;
322
323                 stlen++; /* null terminator '\0' */
324
325                 prop->data.pointer = MEM_callocN(stlen, "id property string 2");
326                 prop->len = prop->totallen = stlen;
327                 BLI_strncpy(prop->data.pointer, st, stlen);
328         }
329
330         prop->type = IDP_STRING;
331         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
332
333         return prop;
334 }
335
336 static IDProperty *IDP_CopyString(IDProperty *prop)
337 {
338         IDProperty *newp = idp_generic_copy(prop);
339
340         if (prop->data.pointer) newp->data.pointer = MEM_dupallocN(prop->data.pointer);
341         newp->len = prop->len;
342         newp->subtype = prop->subtype;
343         newp->totallen = prop->totallen;
344
345         return newp;
346 }
347
348
349 void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
350 {
351         int stlen = strlen(st);
352
353         if (maxlen > 0 && maxlen < stlen)
354                 stlen = maxlen;
355
356         if (prop->subtype == IDP_STRING_SUB_BYTE) {
357                 IDP_ResizeArray(prop, stlen);
358                 memcpy(prop->data.pointer, st, stlen);
359         }
360         else {
361                 stlen++; /* make room for null byte */
362                 IDP_ResizeArray(prop, stlen);
363                 BLI_strncpy(prop->data.pointer, st, stlen);
364         }
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 concatenation 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 (strcmp(loop->name, prop->name) == 0) {
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 properly 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                 IDP_FreeProperty(loop);
587         }
588         BLI_freelistN(&prop->data.group);
589 }
590
591
592 /*-------- Main Functions --------*/
593 IDProperty *IDP_CopyProperty(IDProperty *prop)
594 {
595         switch (prop->type) {
596                 case IDP_GROUP: return IDP_CopyGroup(prop);
597                 case IDP_STRING: return IDP_CopyString(prop);
598                 case IDP_ARRAY: return IDP_CopyArray(prop);
599                 case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
600                 default: return idp_generic_copy(prop);
601         }
602 }
603
604 IDProperty *IDP_GetProperties(ID *id, int create_if_needed)
605 {
606         if (id->properties) return id->properties;
607         else {
608                 if (create_if_needed) {
609                         id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
610                         id->properties->type = IDP_GROUP;
611                         /* don't overwrite the data's name and type
612                          * some functions might need this if they
613                          * don't have a real ID, should be named elsewhere - Campbell */
614                         /* strcpy(id->name, "top_level_group");*/
615                 }
616                 return id->properties;
617         }
618 }
619
620 int IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
621 {
622         if (prop1 == NULL && prop2 == NULL)
623                 return 1;
624         else if (prop1 == NULL || prop2 == NULL)
625                 return 0;
626         else if (prop1->type != prop2->type)
627                 return 0;
628
629         if (prop1->type == IDP_INT)
630                 return (IDP_Int(prop1) == IDP_Int(prop2));
631         else if (prop1->type == IDP_FLOAT)
632                 return (IDP_Float(prop1) == IDP_Float(prop2));
633         else if (prop1->type == IDP_DOUBLE)
634                 return (IDP_Double(prop1) == IDP_Double(prop2));
635         else if (prop1->type == IDP_STRING)
636                 return ((prop1->len == prop2->len) && strncmp(IDP_String(prop1), IDP_String(prop2), prop1->len) == 0);
637         else if (prop1->type == IDP_ARRAY) {
638                 if (prop1->len == prop2->len && prop1->subtype == prop2->subtype)
639                         return memcmp(IDP_Array(prop1), IDP_Array(prop2), idp_size_table[(int)prop1->subtype] * prop1->len);
640                 else
641                         return 0;
642         }
643         else if (prop1->type == IDP_GROUP) {
644                 IDProperty *link1, *link2;
645
646                 if (BLI_countlist(&prop1->data.group) != BLI_countlist(&prop2->data.group))
647                         return 0;
648
649                 for (link1 = prop1->data.group.first; link1; link1 = link1->next) {
650                         link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
651
652                         if (!IDP_EqualsProperties(link1, link2))
653                                 return 0;
654                 }
655
656                 return 1;
657         }
658         else if (prop1->type == IDP_IDPARRAY) {
659                 IDProperty *array1 = IDP_IDPArray(prop1);
660                 IDProperty *array2 = IDP_IDPArray(prop2);
661                 int i;
662
663                 if (prop1->len != prop2->len)
664                         return 0;
665                 
666                 for (i = 0; i < prop1->len; i++)
667                         if (!IDP_EqualsProperties(&array1[i], &array2[i]))
668                                 return 0;
669         }
670         
671         return 1;
672 }
673
674 /* 'val' is never NULL, don't check */
675 IDProperty *IDP_New(const int type, const 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) ||
696                              (val->array.type == IDP_INT) ||
697                              (val->array.type == IDP_DOUBLE) ||
698                              (val->array.type == IDP_GROUP) )
699                         {
700                                 prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
701                                 prop->subtype = val->array.type;
702                                 if (val->array.len)
703                                         prop->data.pointer = MEM_callocN(idp_size_table[val->array.type] * val->array.len, "id property array");
704                                 prop->len = prop->totallen = val->array.len;
705                                 break;
706                         }
707                         else {
708                                 return NULL;
709                         }
710                 }
711                 case IDP_STRING:
712                 {
713                         const char *st = val->string.str;
714
715                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
716                         if (val->string.subtype == IDP_STRING_SUB_BYTE) {
717                                 /* note, intentionally not null terminated */
718                                 if (st == NULL) {
719                                         prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
720                                         prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
721                                         prop->len = 0;
722                                 }
723                                 else {
724                                         prop->data.pointer = MEM_mallocN(val->string.len, "id property string 2");
725                                         prop->len = prop->totallen = val->string.len;
726                                         memcpy(prop->data.pointer, st, val->string.len);
727                                 }
728                                 prop->subtype = IDP_STRING_SUB_BYTE;
729                         }
730                         else {
731                                 if (st == NULL) {
732                                         prop->data.pointer = MEM_callocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
733                                         prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
734                                         prop->len = 1; /*NULL string, has len of 1 to account for null byte.*/
735                                 }
736                                 else {
737                                         int stlen = strlen(st) + 1;
738                                         prop->data.pointer = MEM_mallocN(stlen, "id property string 3");
739                                         prop->len = prop->totallen = stlen;
740                                         memcpy(prop->data.pointer, st, stlen);
741                                 }
742                                 prop->subtype = IDP_STRING_SUB_UTF8;
743                         }
744                         break;
745                 }
746                 case IDP_GROUP:
747                 {
748                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
749                         /* heh I think all needed values are set properly by calloc anyway :) */
750                         break;
751                 }
752                 default:
753                 {
754                         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
755                         break;
756                 }
757         }
758
759         prop->type = type;
760         BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
761         
762         return prop;
763 }
764
765 /* NOTE: this will free all child properties including list arrays and groups!
766  * Also, note that this does NOT unlink anything!  Plus it doesn't free
767  * the actual IDProperty struct either.*/
768 void IDP_FreeProperty(IDProperty *prop)
769 {
770         switch (prop->type) {
771                 case IDP_ARRAY:
772                         IDP_FreeArray(prop);
773                         break;
774                 case IDP_STRING:
775                         IDP_FreeString(prop);
776                         break;
777                 case IDP_GROUP:
778                         IDP_FreeGroup(prop);
779                         break;
780                 case IDP_IDPARRAY:
781                         IDP_FreeIDPArray(prop);
782                         break;
783         }
784 }
785
786 /* Unlinks any IDProperty<->ID linkage that might be going on.
787  * note: currently unused.*/
788 void IDP_UnlinkProperty(IDProperty *prop)
789 {
790         switch (prop->type) {
791                 case IDP_ID:
792                         IDP_UnlinkID(prop);
793         }
794 }