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