1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup bke
22  */
23 
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "BLI_endian_switch.h"
30 #include "BLI_listbase.h"
31 #include "BLI_math.h"
32 #include "BLI_string.h"
33 #include "BLI_utildefines.h"
34 
35 #include "BKE_global.h"
36 #include "BKE_idprop.h"
37 #include "BKE_lib_id.h"
38 
39 #include "CLG_log.h"
40 
41 #include "MEM_guardedalloc.h"
42 
43 #include "BLO_read_write.h"
44 
45 #include "BLI_strict_flags.h"
46 
47 /* IDPropertyTemplate is a union in DNA_ID.h */
48 
49 /**
50  * if the new is 'IDP_ARRAY_REALLOC_LIMIT' items less,
51  * than #IDProperty.totallen, reallocate anyway.
52  */
53 #define IDP_ARRAY_REALLOC_LIMIT 200
54 
55 static CLG_LogRef LOG = {"bke.idprop"};
56 
57 /*local size table.*/
58 static size_t idp_size_table[] = {
59     1, /*strings*/
60     sizeof(int),
61     sizeof(float),
62     sizeof(float[3]),  /*Vector type, deprecated*/
63     sizeof(float[16]), /*Matrix type, deprecated*/
64     0,                 /*arrays don't have a fixed size*/
65     sizeof(ListBase),  /*Group type*/
66     sizeof(void *),
67     sizeof(double),
68 };
69 
70 /* -------------------------------------------------------------------- */
71 /* Array Functions */
72 
73 /** \name IDP Array API
74  * \{ */
75 
76 #define GETPROP(prop, i) &(IDP_IDPArray(prop)[i])
77 
78 /* --------- property array type -------------*/
79 
80 /**
81  * \note as a start to move away from the stupid IDP_New function, this type
82  * has its own allocation function.
83  */
IDP_NewIDPArray(const char * name)84 IDProperty *IDP_NewIDPArray(const char *name)
85 {
86   IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty prop array");
87   prop->type = IDP_IDPARRAY;
88   prop->len = 0;
89   BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
90 
91   return prop;
92 }
93 
IDP_CopyIDPArray(const IDProperty * array,const int flag)94 IDProperty *IDP_CopyIDPArray(const IDProperty *array, const int flag)
95 {
96   /* don't use MEM_dupallocN because this may be part of an array */
97   IDProperty *narray, *tmp;
98 
99   BLI_assert(array->type == IDP_IDPARRAY);
100 
101   narray = MEM_mallocN(sizeof(IDProperty), __func__);
102   *narray = *array;
103 
104   narray->data.pointer = MEM_dupallocN(array->data.pointer);
105   for (int i = 0; i < narray->len; i++) {
106     /* ok, the copy functions always allocate a new structure,
107      * which doesn't work here.  instead, simply copy the
108      * contents of the new structure into the array cell,
109      * then free it.  this makes for more maintainable
110      * code than simply re-implementing the copy functions
111      * in this loop.*/
112     tmp = IDP_CopyProperty_ex(GETPROP(narray, i), flag);
113     memcpy(GETPROP(narray, i), tmp, sizeof(IDProperty));
114     MEM_freeN(tmp);
115   }
116 
117   return narray;
118 }
119 
IDP_FreeIDPArray(IDProperty * prop,const bool do_id_user)120 static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user)
121 {
122   BLI_assert(prop->type == IDP_IDPARRAY);
123 
124   for (int i = 0; i < prop->len; i++) {
125     IDP_FreePropertyContent_ex(GETPROP(prop, i), do_id_user);
126   }
127 
128   if (prop->data.pointer) {
129     MEM_freeN(prop->data.pointer);
130   }
131 }
132 
133 /* shallow copies item */
IDP_SetIndexArray(IDProperty * prop,int index,IDProperty * item)134 void IDP_SetIndexArray(IDProperty *prop, int index, IDProperty *item)
135 {
136   IDProperty *old;
137 
138   BLI_assert(prop->type == IDP_IDPARRAY);
139 
140   if (index >= prop->len || index < 0) {
141     return;
142   }
143 
144   old = GETPROP(prop, index);
145   if (item != old) {
146     IDP_FreePropertyContent(old);
147 
148     memcpy(old, item, sizeof(IDProperty));
149   }
150 }
151 
IDP_GetIndexArray(IDProperty * prop,int index)152 IDProperty *IDP_GetIndexArray(IDProperty *prop, int index)
153 {
154   BLI_assert(prop->type == IDP_IDPARRAY);
155 
156   return GETPROP(prop, index);
157 }
158 
IDP_AppendArray(IDProperty * prop,IDProperty * item)159 void IDP_AppendArray(IDProperty *prop, IDProperty *item)
160 {
161   BLI_assert(prop->type == IDP_IDPARRAY);
162 
163   IDP_ResizeIDPArray(prop, prop->len + 1);
164   IDP_SetIndexArray(prop, prop->len - 1, item);
165 }
166 
IDP_ResizeIDPArray(IDProperty * prop,int newlen)167 void IDP_ResizeIDPArray(IDProperty *prop, int newlen)
168 {
169   int newsize;
170 
171   BLI_assert(prop->type == IDP_IDPARRAY);
172 
173   /* first check if the array buffer size has room */
174   if (newlen <= prop->totallen) {
175     if (newlen < prop->len && prop->totallen - newlen < IDP_ARRAY_REALLOC_LIMIT) {
176       for (int i = newlen; i < prop->len; i++) {
177         IDP_FreePropertyContent(GETPROP(prop, i));
178       }
179 
180       prop->len = newlen;
181       return;
182     }
183     if (newlen >= prop->len) {
184       prop->len = newlen;
185       return;
186     }
187   }
188 
189   /* free trailing items */
190   if (newlen < prop->len) {
191     /* newlen is smaller */
192     for (int i = newlen; i < prop->len; i++) {
193       IDP_FreePropertyContent(GETPROP(prop, i));
194     }
195   }
196 
197   /* - Note: This code comes from python, here's the corresponding comment. - */
198   /* This over-allocates proportional to the list size, making room
199    * for additional growth.  The over-allocation is mild, but is
200    * enough to give linear-time amortized behavior over a long
201    * sequence of appends() in the presence of a poorly-performing
202    * system realloc().
203    * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
204    */
205   newsize = newlen;
206   newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
207   prop->data.pointer = MEM_recallocN(prop->data.pointer, sizeof(IDProperty) * (size_t)newsize);
208   prop->len = newlen;
209   prop->totallen = newsize;
210 }
211 
212 /* ----------- Numerical Array Type ----------- */
idp_resize_group_array(IDProperty * prop,int newlen,void * newarr)213 static void idp_resize_group_array(IDProperty *prop, int newlen, void *newarr)
214 {
215   if (prop->subtype != IDP_GROUP) {
216     return;
217   }
218 
219   if (newlen >= prop->len) {
220     /* bigger */
221     IDProperty **array = newarr;
222     IDPropertyTemplate val;
223     int a;
224 
225     for (a = prop->len; a < newlen; a++) {
226       val.i = 0; /* silence MSVC warning about uninitialized var when debugging */
227       array[a] = IDP_New(IDP_GROUP, &val, "IDP_ResizeArray group");
228     }
229   }
230   else {
231     /* smaller */
232     IDProperty **array = prop->data.pointer;
233     int a;
234 
235     for (a = newlen; a < prop->len; a++) {
236       IDP_FreeProperty(array[a]);
237     }
238   }
239 }
240 
241 /*this function works for strings too!*/
IDP_ResizeArray(IDProperty * prop,int newlen)242 void IDP_ResizeArray(IDProperty *prop, int newlen)
243 {
244   int newsize;
245   const bool is_grow = newlen >= prop->len;
246 
247   /* first check if the array buffer size has room */
248   if (newlen <= prop->totallen && prop->totallen - newlen < IDP_ARRAY_REALLOC_LIMIT) {
249     idp_resize_group_array(prop, newlen, prop->data.pointer);
250     prop->len = newlen;
251     return;
252   }
253 
254   /* - Note: This code comes from python, here's the corresponding comment. - */
255   /* This over-allocates proportional to the list size, making room
256    * for additional growth.  The over-allocation is mild, but is
257    * enough to give linear-time amortized behavior over a long
258    * sequence of appends() in the presence of a poorly-performing
259    * system realloc().
260    * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
261    */
262   newsize = newlen;
263   newsize = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize;
264 
265   if (is_grow == false) {
266     idp_resize_group_array(prop, newlen, prop->data.pointer);
267   }
268 
269   prop->data.pointer = MEM_recallocN(prop->data.pointer,
270                                      idp_size_table[(int)prop->subtype] * (size_t)newsize);
271 
272   if (is_grow == true) {
273     idp_resize_group_array(prop, newlen, prop->data.pointer);
274   }
275 
276   prop->len = newlen;
277   prop->totallen = newsize;
278 }
279 
IDP_FreeArray(IDProperty * prop)280 void IDP_FreeArray(IDProperty *prop)
281 {
282   if (prop->data.pointer) {
283     idp_resize_group_array(prop, 0, NULL);
284     MEM_freeN(prop->data.pointer);
285   }
286 }
287 
idp_generic_copy(const IDProperty * prop,const int UNUSED (flag))288 static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(flag))
289 {
290   IDProperty *newp = MEM_callocN(sizeof(IDProperty), __func__);
291 
292   BLI_strncpy(newp->name, prop->name, MAX_IDPROP_NAME);
293   newp->type = prop->type;
294   newp->flag = prop->flag;
295   newp->data.val = prop->data.val;
296   newp->data.val2 = prop->data.val2;
297 
298   return newp;
299 }
300 
IDP_CopyArray(const IDProperty * prop,const int flag)301 static IDProperty *IDP_CopyArray(const IDProperty *prop, const int flag)
302 {
303   IDProperty *newp = idp_generic_copy(prop, flag);
304 
305   if (prop->data.pointer) {
306     newp->data.pointer = MEM_dupallocN(prop->data.pointer);
307 
308     if (prop->type == IDP_GROUP) {
309       IDProperty **array = newp->data.pointer;
310       int a;
311 
312       for (a = 0; a < prop->len; a++) {
313         array[a] = IDP_CopyProperty_ex(array[a], flag);
314       }
315     }
316   }
317   newp->len = prop->len;
318   newp->subtype = prop->subtype;
319   newp->totallen = prop->totallen;
320 
321   return newp;
322 }
323 /** \} */
324 
325 /* -------------------------------------------------------------------- */
326 /* String Functions */
327 
328 /** \name IDProperty String API
329  * \{ */
330 
331 /**
332  *
333  * \param st: The string to assign.
334  * \param name: The property name.
335  * \param maxlen: The size of the new string (including the \0 terminator).
336  * \return The new string property.
337  */
IDP_NewString(const char * st,const char * name,int maxlen)338 IDProperty *IDP_NewString(const char *st, const char *name, int maxlen)
339 {
340   IDProperty *prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
341 
342   if (st == NULL) {
343     prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
344     *IDP_String(prop) = '\0';
345     prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
346     prop->len = 1; /* NULL string, has len of 1 to account for null byte. */
347   }
348   else {
349     /* include null terminator '\0' */
350     int stlen = (int)strlen(st) + 1;
351 
352     if (maxlen > 0 && maxlen < stlen) {
353       stlen = maxlen;
354     }
355 
356     prop->data.pointer = MEM_mallocN((size_t)stlen, "id property string 2");
357     prop->len = prop->totallen = stlen;
358     BLI_strncpy(prop->data.pointer, st, (size_t)stlen);
359   }
360 
361   prop->type = IDP_STRING;
362   BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
363 
364   return prop;
365 }
366 
IDP_CopyString(const IDProperty * prop,const int flag)367 static IDProperty *IDP_CopyString(const IDProperty *prop, const int flag)
368 {
369   IDProperty *newp;
370 
371   BLI_assert(prop->type == IDP_STRING);
372   newp = idp_generic_copy(prop, flag);
373 
374   if (prop->data.pointer) {
375     newp->data.pointer = MEM_dupallocN(prop->data.pointer);
376   }
377   newp->len = prop->len;
378   newp->subtype = prop->subtype;
379   newp->totallen = prop->totallen;
380 
381   return newp;
382 }
383 
IDP_AssignString(IDProperty * prop,const char * st,int maxlen)384 void IDP_AssignString(IDProperty *prop, const char *st, int maxlen)
385 {
386   int stlen;
387 
388   BLI_assert(prop->type == IDP_STRING);
389   stlen = (int)strlen(st);
390   if (maxlen > 0 && maxlen < stlen) {
391     stlen = maxlen;
392   }
393 
394   if (prop->subtype == IDP_STRING_SUB_BYTE) {
395     IDP_ResizeArray(prop, stlen);
396     memcpy(prop->data.pointer, st, (size_t)stlen);
397   }
398   else {
399     stlen++;
400     IDP_ResizeArray(prop, stlen);
401     BLI_strncpy(prop->data.pointer, st, (size_t)stlen);
402   }
403 }
404 
IDP_ConcatStringC(IDProperty * prop,const char * st)405 void IDP_ConcatStringC(IDProperty *prop, const char *st)
406 {
407   int newlen;
408 
409   BLI_assert(prop->type == IDP_STRING);
410 
411   newlen = prop->len + (int)strlen(st);
412   /* we have to remember that prop->len includes the null byte for strings.
413    * so there's no need to add +1 to the resize function.*/
414   IDP_ResizeArray(prop, newlen);
415   strcat(prop->data.pointer, st);
416 }
417 
IDP_ConcatString(IDProperty * str1,IDProperty * append)418 void IDP_ConcatString(IDProperty *str1, IDProperty *append)
419 {
420   int newlen;
421 
422   BLI_assert(append->type == IDP_STRING);
423 
424   /* since ->len for strings includes the NULL byte, we have to subtract one or
425    * we'll get an extra null byte after each concatenation operation.*/
426   newlen = str1->len + append->len - 1;
427   IDP_ResizeArray(str1, newlen);
428   strcat(str1->data.pointer, append->data.pointer);
429 }
430 
IDP_FreeString(IDProperty * prop)431 void IDP_FreeString(IDProperty *prop)
432 {
433   BLI_assert(prop->type == IDP_STRING);
434 
435   if (prop->data.pointer) {
436     MEM_freeN(prop->data.pointer);
437   }
438 }
439 /** \} */
440 
441 /* -------------------------------------------------------------------- */
442 /* ID Type */
443 
444 /** \name IDProperty ID API
445  * \{ */
446 
IDP_CopyID(const IDProperty * prop,const int flag)447 static IDProperty *IDP_CopyID(const IDProperty *prop, const int flag)
448 {
449   IDProperty *newp;
450 
451   BLI_assert(prop->type == IDP_ID);
452   newp = idp_generic_copy(prop, flag);
453 
454   newp->data.pointer = prop->data.pointer;
455   if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
456     id_us_plus(IDP_Id(newp));
457   }
458 
459   return newp;
460 }
461 
IDP_AssignID(IDProperty * prop,ID * id,const int flag)462 void IDP_AssignID(IDProperty *prop, ID *id, const int flag)
463 {
464   BLI_assert(prop->type == IDP_ID);
465 
466   if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0 && IDP_Id(prop) != NULL) {
467     id_us_min(IDP_Id(prop));
468   }
469 
470   prop->data.pointer = id;
471 
472   if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
473     id_us_plus(IDP_Id(prop));
474   }
475 }
476 
477 /** \} */
478 
479 /* -------------------------------------------------------------------- */
480 /* Group Functions */
481 
482 /** \name IDProperty Group API
483  * \{ */
484 
485 /**
486  * Checks if a property with the same name as prop exists, and if so replaces it.
487  */
IDP_CopyGroup(const IDProperty * prop,const int flag)488 static IDProperty *IDP_CopyGroup(const IDProperty *prop, const int flag)
489 {
490   IDProperty *newp, *link;
491 
492   BLI_assert(prop->type == IDP_GROUP);
493   newp = idp_generic_copy(prop, flag);
494   newp->len = prop->len;
495   newp->subtype = prop->subtype;
496 
497   for (link = prop->data.group.first; link; link = link->next) {
498     BLI_addtail(&newp->data.group, IDP_CopyProperty_ex(link, flag));
499   }
500 
501   return newp;
502 }
503 
504 /* use for syncing proxies.
505  * When values name and types match, copy the values, else ignore */
IDP_SyncGroupValues(IDProperty * dest,const IDProperty * src)506 void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src)
507 {
508   IDProperty *other, *prop;
509 
510   BLI_assert(dest->type == IDP_GROUP);
511   BLI_assert(src->type == IDP_GROUP);
512 
513   for (prop = src->data.group.first; prop; prop = prop->next) {
514     other = BLI_findstring(&dest->data.group, prop->name, offsetof(IDProperty, name));
515     if (other && prop->type == other->type) {
516       switch (prop->type) {
517         case IDP_INT:
518         case IDP_FLOAT:
519         case IDP_DOUBLE:
520           other->data = prop->data;
521           break;
522         case IDP_GROUP:
523           IDP_SyncGroupValues(other, prop);
524           break;
525         default: {
526           BLI_insertlinkreplace(&dest->data.group, other, IDP_CopyProperty(prop));
527           IDP_FreeProperty(other);
528           break;
529         }
530       }
531     }
532   }
533 }
534 
IDP_SyncGroupTypes(IDProperty * dest,const IDProperty * src,const bool do_arraylen)535 void IDP_SyncGroupTypes(IDProperty *dest, const IDProperty *src, const bool do_arraylen)
536 {
537   IDProperty *prop_dst, *prop_dst_next;
538   const IDProperty *prop_src;
539 
540   for (prop_dst = dest->data.group.first; prop_dst; prop_dst = prop_dst_next) {
541     prop_dst_next = prop_dst->next;
542     if ((prop_src = IDP_GetPropertyFromGroup((IDProperty *)src, prop_dst->name))) {
543       /* check of we should replace? */
544       if ((prop_dst->type != prop_src->type || prop_dst->subtype != prop_src->subtype) ||
545           (do_arraylen && ELEM(prop_dst->type, IDP_ARRAY, IDP_IDPARRAY) &&
546            (prop_src->len != prop_dst->len))) {
547         BLI_insertlinkreplace(&dest->data.group, prop_dst, IDP_CopyProperty(prop_src));
548         IDP_FreeProperty(prop_dst);
549       }
550       else if (prop_dst->type == IDP_GROUP) {
551         IDP_SyncGroupTypes(prop_dst, prop_src, do_arraylen);
552       }
553     }
554     else {
555       IDP_FreeFromGroup(dest, prop_dst);
556     }
557   }
558 }
559 
560 /**
561  * Replaces all properties with the same name in a destination group from a source group.
562  */
IDP_ReplaceGroupInGroup(IDProperty * dest,const IDProperty * src)563 void IDP_ReplaceGroupInGroup(IDProperty *dest, const IDProperty *src)
564 {
565   IDProperty *loop, *prop;
566 
567   BLI_assert(dest->type == IDP_GROUP);
568   BLI_assert(src->type == IDP_GROUP);
569 
570   for (prop = src->data.group.first; prop; prop = prop->next) {
571     for (loop = dest->data.group.first; loop; loop = loop->next) {
572       if (STREQ(loop->name, prop->name)) {
573         BLI_insertlinkreplace(&dest->data.group, loop, IDP_CopyProperty(prop));
574         IDP_FreeProperty(loop);
575         break;
576       }
577     }
578 
579     /* only add at end if not added yet */
580     if (loop == NULL) {
581       IDProperty *copy = IDP_CopyProperty(prop);
582       dest->len++;
583       BLI_addtail(&dest->data.group, copy);
584     }
585   }
586 }
587 
588 /**
589  * Checks if a property with the same name as prop exists, and if so replaces it.
590  * Use this to preserve order!
591  */
IDP_ReplaceInGroup_ex(IDProperty * group,IDProperty * prop,IDProperty * prop_exist)592 void IDP_ReplaceInGroup_ex(IDProperty *group, IDProperty *prop, IDProperty *prop_exist)
593 {
594   BLI_assert(group->type == IDP_GROUP);
595   BLI_assert(prop_exist == IDP_GetPropertyFromGroup(group, prop->name));
596 
597   if (prop_exist != NULL) {
598     BLI_insertlinkreplace(&group->data.group, prop_exist, prop);
599     IDP_FreeProperty(prop_exist);
600   }
601   else {
602     group->len++;
603     BLI_addtail(&group->data.group, prop);
604   }
605 }
606 
IDP_ReplaceInGroup(IDProperty * group,IDProperty * prop)607 void IDP_ReplaceInGroup(IDProperty *group, IDProperty *prop)
608 {
609   IDProperty *prop_exist = IDP_GetPropertyFromGroup(group, prop->name);
610 
611   IDP_ReplaceInGroup_ex(group, prop, prop_exist);
612 }
613 
614 /**
615  * If a property is missing in \a dest, add it.
616  * Do it recursively.
617  */
IDP_MergeGroup_ex(IDProperty * dest,const IDProperty * src,const bool do_overwrite,const int flag)618 void IDP_MergeGroup_ex(IDProperty *dest,
619                        const IDProperty *src,
620                        const bool do_overwrite,
621                        const int flag)
622 {
623   IDProperty *prop;
624 
625   BLI_assert(dest->type == IDP_GROUP);
626   BLI_assert(src->type == IDP_GROUP);
627 
628   if (do_overwrite) {
629     for (prop = src->data.group.first; prop; prop = prop->next) {
630       if (prop->type == IDP_GROUP) {
631         IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name);
632 
633         if (prop_exist != NULL) {
634           IDP_MergeGroup_ex(prop_exist, prop, do_overwrite, flag);
635           continue;
636         }
637       }
638 
639       IDProperty *copy = IDP_CopyProperty_ex(prop, flag);
640       IDP_ReplaceInGroup(dest, copy);
641     }
642   }
643   else {
644     for (prop = src->data.group.first; prop; prop = prop->next) {
645       IDProperty *prop_exist = IDP_GetPropertyFromGroup(dest, prop->name);
646       if (prop_exist != NULL) {
647         if (prop->type == IDP_GROUP) {
648           IDP_MergeGroup_ex(prop_exist, prop, do_overwrite, flag);
649           continue;
650         }
651       }
652       else {
653         IDProperty *copy = IDP_CopyProperty_ex(prop, flag);
654         dest->len++;
655         BLI_addtail(&dest->data.group, copy);
656       }
657     }
658   }
659 }
660 
661 /**
662  * If a property is missing in \a dest, add it.
663  * Do it recursively.
664  */
IDP_MergeGroup(IDProperty * dest,const IDProperty * src,const bool do_overwrite)665 void IDP_MergeGroup(IDProperty *dest, const IDProperty *src, const bool do_overwrite)
666 {
667   IDP_MergeGroup_ex(dest, src, do_overwrite, 0);
668 }
669 
670 /**
671  * This function has a sanity check to make sure ID properties with the same name don't
672  * get added to the group.
673  *
674  * The sanity check just means the property is not added to the group if another property
675  * exists with the same name; the client code using ID properties then needs to detect this
676  * (the function that adds new properties to groups, #IDP_AddToGroup,
677  * returns false if a property can't be added to the group, and true if it can)
678  * and free the property.
679  */
IDP_AddToGroup(IDProperty * group,IDProperty * prop)680 bool IDP_AddToGroup(IDProperty *group, IDProperty *prop)
681 {
682   BLI_assert(group->type == IDP_GROUP);
683 
684   if (IDP_GetPropertyFromGroup(group, prop->name) == NULL) {
685     group->len++;
686     BLI_addtail(&group->data.group, prop);
687     return true;
688   }
689 
690   return false;
691 }
692 
693 /**
694  * This is the same as IDP_AddToGroup, only you pass an item
695  * in the group list to be inserted after.
696  */
IDP_InsertToGroup(IDProperty * group,IDProperty * previous,IDProperty * pnew)697 bool IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew)
698 {
699   BLI_assert(group->type == IDP_GROUP);
700 
701   if (IDP_GetPropertyFromGroup(group, pnew->name) == NULL) {
702     group->len++;
703     BLI_insertlinkafter(&group->data.group, previous, pnew);
704     return true;
705   }
706 
707   return false;
708 }
709 
710 /**
711  * \note this does not free the property!!
712  *
713  * To free the property, you have to do:
714  * IDP_FreeProperty(prop);
715  */
IDP_RemoveFromGroup(IDProperty * group,IDProperty * prop)716 void IDP_RemoveFromGroup(IDProperty *group, IDProperty *prop)
717 {
718   BLI_assert(group->type == IDP_GROUP);
719 
720   group->len--;
721   BLI_remlink(&group->data.group, prop);
722 }
723 
724 /**
725  * Removes the property from the group and frees it.
726  */
IDP_FreeFromGroup(IDProperty * group,IDProperty * prop)727 void IDP_FreeFromGroup(IDProperty *group, IDProperty *prop)
728 {
729   IDP_RemoveFromGroup(group, prop);
730   IDP_FreeProperty(prop);
731 }
732 
IDP_GetPropertyFromGroup(const IDProperty * prop,const char * name)733 IDProperty *IDP_GetPropertyFromGroup(const IDProperty *prop, const char *name)
734 {
735   BLI_assert(prop->type == IDP_GROUP);
736 
737   return (IDProperty *)BLI_findstring(&prop->data.group, name, offsetof(IDProperty, name));
738 }
739 /** same as above but ensure type match */
IDP_GetPropertyTypeFromGroup(const IDProperty * prop,const char * name,const char type)740 IDProperty *IDP_GetPropertyTypeFromGroup(const IDProperty *prop, const char *name, const char type)
741 {
742   IDProperty *idprop = IDP_GetPropertyFromGroup(prop, name);
743   return (idprop && idprop->type == type) ? idprop : NULL;
744 }
745 
746 /* Ok, the way things work, Groups free the ID Property structs of their children.
747  * This is because all ID Property freeing functions free only direct data (not the ID Property
748  * struct itself), but for Groups the child properties *are* considered
749  * direct data. */
IDP_FreeGroup(IDProperty * prop,const bool do_id_user)750 static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
751 {
752   IDProperty *loop;
753 
754   BLI_assert(prop->type == IDP_GROUP);
755   for (loop = prop->data.group.first; loop; loop = loop->next) {
756     IDP_FreePropertyContent_ex(loop, do_id_user);
757   }
758   BLI_freelistN(&prop->data.group);
759 }
760 /** \} */
761 
762 /* -------------------------------------------------------------------- */
763 /* Main Functions */
764 
765 /** \name IDProperty Main API
766  * \{ */
IDP_CopyProperty_ex(const IDProperty * prop,const int flag)767 IDProperty *IDP_CopyProperty_ex(const IDProperty *prop, const int flag)
768 {
769   switch (prop->type) {
770     case IDP_GROUP:
771       return IDP_CopyGroup(prop, flag);
772     case IDP_STRING:
773       return IDP_CopyString(prop, flag);
774     case IDP_ID:
775       return IDP_CopyID(prop, flag);
776     case IDP_ARRAY:
777       return IDP_CopyArray(prop, flag);
778     case IDP_IDPARRAY:
779       return IDP_CopyIDPArray(prop, flag);
780     default:
781       return idp_generic_copy(prop, flag);
782   }
783 }
784 
IDP_CopyProperty(const IDProperty * prop)785 IDProperty *IDP_CopyProperty(const IDProperty *prop)
786 {
787   return IDP_CopyProperty_ex(prop, 0);
788 }
789 
790 /**
791  * Copy content from source IDProperty into destination one, freeing destination property's content
792  * first.
793  */
IDP_CopyPropertyContent(IDProperty * dst,IDProperty * src)794 void IDP_CopyPropertyContent(IDProperty *dst, IDProperty *src)
795 {
796   IDProperty *idprop_tmp = IDP_CopyProperty(src);
797   idprop_tmp->prev = dst->prev;
798   idprop_tmp->next = dst->next;
799   SWAP(IDProperty, *dst, *idprop_tmp);
800   IDP_FreeProperty(idprop_tmp);
801 }
802 
803 /* Updates ID pointers after an object has been copied */
804 /* TODO Nuke this once its only user has been correctly converted
805  * to use generic ID management from BKE_library! */
IDP_RelinkProperty(struct IDProperty * prop)806 void IDP_RelinkProperty(struct IDProperty *prop)
807 {
808   if (!prop) {
809     return;
810   }
811 
812   switch (prop->type) {
813     case IDP_GROUP: {
814       LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
815         IDP_RelinkProperty(loop);
816       }
817       break;
818     }
819     case IDP_IDPARRAY: {
820       IDProperty *idp_array = IDP_Array(prop);
821       for (int i = 0; i < prop->len; i++) {
822         IDP_RelinkProperty(&idp_array[i]);
823       }
824       break;
825     }
826     case IDP_ID: {
827       ID *id = IDP_Id(prop);
828       if (id && id->newid) {
829         id_us_min(IDP_Id(prop));
830         prop->data.pointer = id->newid;
831         id_us_plus(IDP_Id(prop));
832       }
833       break;
834     }
835     default:
836       break; /* Nothing to do for other IDProp types. */
837   }
838 }
839 
840 /**
841  * Get the Group property that contains the id properties for ID id.  Set create_if_needed
842  * to create the Group property and attach it to id if it doesn't exist; otherwise
843  * the function will return NULL if there's no Group property attached to the ID.
844  */
IDP_GetProperties(ID * id,const bool create_if_needed)845 IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed)
846 {
847   if (id->properties) {
848     return id->properties;
849   }
850 
851   if (create_if_needed) {
852     id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
853     id->properties->type = IDP_GROUP;
854     /* don't overwrite the data's name and type
855      * some functions might need this if they
856      * don't have a real ID, should be named elsewhere - Campbell */
857     /* strcpy(id->name, "top_level_group");*/
858   }
859   return id->properties;
860 }
861 
862 /**
863  * \param is_strict: When false treat missing items as a match */
IDP_EqualsProperties_ex(IDProperty * prop1,IDProperty * prop2,const bool is_strict)864 bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is_strict)
865 {
866   if (prop1 == NULL && prop2 == NULL) {
867     return true;
868   }
869   if (prop1 == NULL || prop2 == NULL) {
870     return is_strict ? false : true;
871   }
872   if (prop1->type != prop2->type) {
873     return false;
874   }
875 
876   switch (prop1->type) {
877     case IDP_INT:
878       return (IDP_Int(prop1) == IDP_Int(prop2));
879     case IDP_FLOAT:
880 #if !defined(NDEBUG) && defined(WITH_PYTHON)
881     {
882       float p1 = IDP_Float(prop1);
883       float p2 = IDP_Float(prop2);
884       if ((p1 != p2) && ((fabsf(p1 - p2) / max_ff(p1, p2)) < 0.001f)) {
885         printf(
886             "WARNING: Comparing two float properties that have nearly the same value (%f vs. "
887             "%f)\n",
888             p1,
889             p2);
890         printf("    p1: ");
891         IDP_print(prop1);
892         printf("    p2: ");
893         IDP_print(prop2);
894       }
895     }
896 #endif
897       return (IDP_Float(prop1) == IDP_Float(prop2));
898     case IDP_DOUBLE:
899       return (IDP_Double(prop1) == IDP_Double(prop2));
900     case IDP_STRING: {
901       return (((prop1->len == prop2->len) &&
902                STREQLEN(IDP_String(prop1), IDP_String(prop2), (size_t)prop1->len)));
903     }
904     case IDP_ARRAY:
905       if (prop1->len == prop2->len && prop1->subtype == prop2->subtype) {
906         return (memcmp(IDP_Array(prop1),
907                        IDP_Array(prop2),
908                        idp_size_table[(int)prop1->subtype] * (size_t)prop1->len) == 0);
909       }
910       return false;
911     case IDP_GROUP: {
912       IDProperty *link1, *link2;
913 
914       if (is_strict && prop1->len != prop2->len) {
915         return false;
916       }
917 
918       for (link1 = prop1->data.group.first; link1; link1 = link1->next) {
919         link2 = IDP_GetPropertyFromGroup(prop2, link1->name);
920 
921         if (!IDP_EqualsProperties_ex(link1, link2, is_strict)) {
922           return false;
923         }
924       }
925 
926       return true;
927     }
928     case IDP_IDPARRAY: {
929       IDProperty *array1 = IDP_IDPArray(prop1);
930       IDProperty *array2 = IDP_IDPArray(prop2);
931 
932       if (prop1->len != prop2->len) {
933         return false;
934       }
935 
936       for (int i = 0; i < prop1->len; i++) {
937         if (!IDP_EqualsProperties_ex(&array1[i], &array2[i], is_strict)) {
938           return false;
939         }
940       }
941       return true;
942     }
943     case IDP_ID:
944       return (IDP_Id(prop1) == IDP_Id(prop2));
945     default:
946       BLI_assert(0);
947       break;
948   }
949 
950   return true;
951 }
952 
IDP_EqualsProperties(IDProperty * prop1,IDProperty * prop2)953 bool IDP_EqualsProperties(IDProperty *prop1, IDProperty *prop2)
954 {
955   return IDP_EqualsProperties_ex(prop1, prop2, true);
956 }
957 
958 /**
959  * Allocate a new ID.
960  *
961  * This function takes three arguments: the ID property type, a union which defines
962  * its initial value, and a name.
963  *
964  * The union is simple to use; see the top of this header file for its definition.
965  * An example of using this function:
966  *
967  * \code{.c}
968  * IDPropertyTemplate val;
969  * IDProperty *group, *idgroup, *color;
970  * group = IDP_New(IDP_GROUP, val, "group1"); // groups don't need a template.
971  *
972  * val.array.len = 4
973  * val.array.type = IDP_FLOAT;
974  * color = IDP_New(IDP_ARRAY, val, "color1");
975  *
976  * idgroup = IDP_GetProperties(some_id, 1);
977  * IDP_AddToGroup(idgroup, color);
978  * IDP_AddToGroup(idgroup, group);
979  * \endcode
980  *
981  * Note that you MUST either attach the id property to an id property group with
982  * IDP_AddToGroup or MEM_freeN the property, doing anything else might result in
983  * a memory leak.
984  */
IDP_New(const char type,const IDPropertyTemplate * val,const char * name)985 IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *name)
986 {
987   IDProperty *prop = NULL;
988 
989   switch (type) {
990     case IDP_INT:
991       prop = MEM_callocN(sizeof(IDProperty), "IDProperty int");
992       prop->data.val = val->i;
993       break;
994     case IDP_FLOAT:
995       prop = MEM_callocN(sizeof(IDProperty), "IDProperty float");
996       *(float *)&prop->data.val = val->f;
997       break;
998     case IDP_DOUBLE:
999       prop = MEM_callocN(sizeof(IDProperty), "IDProperty double");
1000       *(double *)&prop->data.val = val->d;
1001       break;
1002     case IDP_ARRAY: {
1003       /* for now, we only support float and int and double arrays */
1004       if ((val->array.type == IDP_FLOAT) || (val->array.type == IDP_INT) ||
1005           (val->array.type == IDP_DOUBLE) || (val->array.type == IDP_GROUP)) {
1006         prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
1007         prop->subtype = val->array.type;
1008         if (val->array.len) {
1009           prop->data.pointer = MEM_callocN(
1010               idp_size_table[val->array.type] * (size_t)val->array.len, "id property array");
1011         }
1012         prop->len = prop->totallen = val->array.len;
1013         break;
1014       }
1015       CLOG_ERROR(&LOG, "bad array type.");
1016       return NULL;
1017     }
1018     case IDP_STRING: {
1019       const char *st = val->string.str;
1020 
1021       prop = MEM_callocN(sizeof(IDProperty), "IDProperty string");
1022       if (val->string.subtype == IDP_STRING_SUB_BYTE) {
1023         /* note, intentionally not null terminated */
1024         if (st == NULL) {
1025           prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
1026           *IDP_String(prop) = '\0';
1027           prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
1028           prop->len = 0;
1029         }
1030         else {
1031           prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 2");
1032           prop->len = prop->totallen = val->string.len;
1033           memcpy(prop->data.pointer, st, (size_t)val->string.len);
1034         }
1035         prop->subtype = IDP_STRING_SUB_BYTE;
1036       }
1037       else {
1038         if (st == NULL || val->string.len <= 1) {
1039           prop->data.pointer = MEM_mallocN(DEFAULT_ALLOC_FOR_NULL_STRINGS, "id property string 1");
1040           *IDP_String(prop) = '\0';
1041           prop->totallen = DEFAULT_ALLOC_FOR_NULL_STRINGS;
1042           /* NULL string, has len of 1 to account for null byte. */
1043           prop->len = 1;
1044         }
1045         else {
1046           BLI_assert((int)val->string.len <= (int)strlen(st) + 1);
1047           prop->data.pointer = MEM_mallocN((size_t)val->string.len, "id property string 3");
1048           memcpy(prop->data.pointer, st, (size_t)val->string.len - 1);
1049           IDP_String(prop)[val->string.len - 1] = '\0';
1050           prop->len = prop->totallen = val->string.len;
1051         }
1052         prop->subtype = IDP_STRING_SUB_UTF8;
1053       }
1054       break;
1055     }
1056     case IDP_GROUP: {
1057       /* Values are set properly by calloc. */
1058       prop = MEM_callocN(sizeof(IDProperty), "IDProperty group");
1059       break;
1060     }
1061     case IDP_ID: {
1062       prop = MEM_callocN(sizeof(IDProperty), "IDProperty datablock");
1063       prop->data.pointer = (void *)val->id;
1064       prop->type = IDP_ID;
1065       id_us_plus(IDP_Id(prop));
1066       break;
1067     }
1068     default: {
1069       prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
1070       break;
1071     }
1072   }
1073 
1074   prop->type = type;
1075   BLI_strncpy(prop->name, name, MAX_IDPROP_NAME);
1076 
1077   return prop;
1078 }
1079 
1080 /**
1081  * \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
1082  * But it does not free the actual IDProperty struct itself.
1083  */
IDP_FreePropertyContent_ex(IDProperty * prop,const bool do_id_user)1084 void IDP_FreePropertyContent_ex(IDProperty *prop, const bool do_id_user)
1085 {
1086   switch (prop->type) {
1087     case IDP_ARRAY:
1088       IDP_FreeArray(prop);
1089       break;
1090     case IDP_STRING:
1091       IDP_FreeString(prop);
1092       break;
1093     case IDP_GROUP:
1094       IDP_FreeGroup(prop, do_id_user);
1095       break;
1096     case IDP_IDPARRAY:
1097       IDP_FreeIDPArray(prop, do_id_user);
1098       break;
1099     case IDP_ID:
1100       if (do_id_user) {
1101         id_us_min(IDP_Id(prop));
1102       }
1103       break;
1104   }
1105 }
1106 
IDP_FreePropertyContent(IDProperty * prop)1107 void IDP_FreePropertyContent(IDProperty *prop)
1108 {
1109   IDP_FreePropertyContent_ex(prop, true);
1110 }
1111 
IDP_FreeProperty_ex(IDProperty * prop,const bool do_id_user)1112 void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user)
1113 {
1114   IDP_FreePropertyContent_ex(prop, do_id_user);
1115   MEM_freeN(prop);
1116 }
1117 
IDP_FreeProperty(IDProperty * prop)1118 void IDP_FreeProperty(IDProperty *prop)
1119 {
1120   IDP_FreePropertyContent(prop);
1121   MEM_freeN(prop);
1122 }
1123 
IDP_ClearProperty(IDProperty * prop)1124 void IDP_ClearProperty(IDProperty *prop)
1125 {
1126   IDP_FreePropertyContent(prop);
1127   prop->data.pointer = NULL;
1128   prop->len = prop->totallen = 0;
1129 }
1130 
IDP_Reset(IDProperty * prop,const IDProperty * reference)1131 void IDP_Reset(IDProperty *prop, const IDProperty *reference)
1132 {
1133   if (prop == NULL) {
1134     return;
1135   }
1136   IDP_ClearProperty(prop);
1137   if (reference != NULL) {
1138     IDP_MergeGroup(prop, reference, true);
1139   }
1140 }
1141 
1142 /**
1143  * Loop through all ID properties in hierarchy of given \a id_property_root included.
1144  *
1145  * \note Container types (groups and arrays) are processed after applying the callback on them.
1146  *
1147  * \param type_filter: If not 0, only apply callback on properties of matching types, see
1148  * IDP_TYPE_FILTER_ enum in DNA_ID.h.
1149  */
IDP_foreach_property(IDProperty * id_property_root,const int type_filter,IDPForeachPropertyCallback callback,void * user_data)1150 void IDP_foreach_property(IDProperty *id_property_root,
1151                           const int type_filter,
1152                           IDPForeachPropertyCallback callback,
1153                           void *user_data)
1154 {
1155   if (!id_property_root) {
1156     return;
1157   }
1158 
1159   if (type_filter == 0 || (1 << id_property_root->type) & type_filter) {
1160     callback(id_property_root, user_data);
1161   }
1162 
1163   /* Recursive call into container types of ID properties. */
1164   switch (id_property_root->type) {
1165     case IDP_GROUP: {
1166       LISTBASE_FOREACH (IDProperty *, loop, &id_property_root->data.group) {
1167         IDP_foreach_property(loop, type_filter, callback, user_data);
1168       }
1169       break;
1170     }
1171     case IDP_IDPARRAY: {
1172       IDProperty *loop = IDP_Array(id_property_root);
1173       for (int i = 0; i < id_property_root->len; i++) {
1174         IDP_foreach_property(&loop[i], type_filter, callback, user_data);
1175       }
1176       break;
1177     }
1178     default:
1179       break; /* Nothing to do here with other types of IDProperties... */
1180   }
1181 }
1182 
1183 void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer);
1184 
IDP_WriteArray(const IDProperty * prop,BlendWriter * writer)1185 static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
1186 {
1187   /*REMEMBER to set totalen to len in the linking code!!*/
1188   if (prop->data.pointer) {
1189     BLO_write_raw(writer, MEM_allocN_len(prop->data.pointer), prop->data.pointer);
1190 
1191     if (prop->subtype == IDP_GROUP) {
1192       IDProperty **array = prop->data.pointer;
1193       int a;
1194 
1195       for (a = 0; a < prop->len; a++) {
1196         IDP_BlendWrite(writer, array[a]);
1197       }
1198     }
1199   }
1200 }
1201 
IDP_WriteIDPArray(const IDProperty * prop,BlendWriter * writer)1202 static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer)
1203 {
1204   /*REMEMBER to set totalen to len in the linking code!!*/
1205   if (prop->data.pointer) {
1206     const IDProperty *array = prop->data.pointer;
1207     int a;
1208 
1209     BLO_write_struct_array(writer, IDProperty, prop->len, array);
1210 
1211     for (a = 0; a < prop->len; a++) {
1212       IDP_WriteProperty_OnlyData(&array[a], writer);
1213     }
1214   }
1215 }
1216 
IDP_WriteString(const IDProperty * prop,BlendWriter * writer)1217 static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer)
1218 {
1219   /*REMEMBER to set totalen to len in the linking code!!*/
1220   BLO_write_raw(writer, (size_t)prop->len, prop->data.pointer);
1221 }
1222 
IDP_WriteGroup(const IDProperty * prop,BlendWriter * writer)1223 static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer)
1224 {
1225   IDProperty *loop;
1226 
1227   for (loop = prop->data.group.first; loop; loop = loop->next) {
1228     IDP_BlendWrite(writer, loop);
1229   }
1230 }
1231 
1232 /* Functions to read/write ID Properties */
IDP_WriteProperty_OnlyData(const IDProperty * prop,BlendWriter * writer)1233 void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
1234 {
1235   switch (prop->type) {
1236     case IDP_GROUP:
1237       IDP_WriteGroup(prop, writer);
1238       break;
1239     case IDP_STRING:
1240       IDP_WriteString(prop, writer);
1241       break;
1242     case IDP_ARRAY:
1243       IDP_WriteArray(prop, writer);
1244       break;
1245     case IDP_IDPARRAY:
1246       IDP_WriteIDPArray(prop, writer);
1247       break;
1248   }
1249 }
1250 
IDP_BlendWrite(BlendWriter * writer,const IDProperty * prop)1251 void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
1252 {
1253   BLO_write_struct(writer, IDProperty, prop);
1254   IDP_WriteProperty_OnlyData(prop, writer);
1255 }
1256 
1257 static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader);
1258 
IDP_DirectLinkIDPArray(IDProperty * prop,BlendDataReader * reader)1259 static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader)
1260 {
1261   IDProperty *array;
1262 
1263   /* since we didn't save the extra buffer, set totallen to len */
1264   prop->totallen = prop->len;
1265   BLO_read_data_address(reader, &prop->data.pointer);
1266 
1267   array = (IDProperty *)prop->data.pointer;
1268 
1269   /* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared
1270    * there's not really anything we can do to correct this, at least don't crash */
1271   if (array == NULL) {
1272     prop->len = 0;
1273     prop->totallen = 0;
1274   }
1275 
1276   for (int i = 0; i < prop->len; i++) {
1277     IDP_DirectLinkProperty(&array[i], reader);
1278   }
1279 }
1280 
IDP_DirectLinkArray(IDProperty * prop,BlendDataReader * reader)1281 static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader)
1282 {
1283   IDProperty **array;
1284 
1285   /* since we didn't save the extra buffer, set totallen to len */
1286   prop->totallen = prop->len;
1287 
1288   if (prop->subtype == IDP_GROUP) {
1289     BLO_read_pointer_array(reader, &prop->data.pointer);
1290     array = prop->data.pointer;
1291 
1292     for (int i = 0; i < prop->len; i++) {
1293       IDP_DirectLinkProperty(array[i], reader);
1294     }
1295   }
1296   else if (prop->subtype == IDP_DOUBLE) {
1297     BLO_read_double_array(reader, prop->len, (double **)&prop->data.pointer);
1298   }
1299   else {
1300     /* also used for floats */
1301     BLO_read_int32_array(reader, prop->len, (int **)&prop->data.pointer);
1302   }
1303 }
1304 
IDP_DirectLinkString(IDProperty * prop,BlendDataReader * reader)1305 static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader)
1306 {
1307   /*since we didn't save the extra string buffer, set totallen to len.*/
1308   prop->totallen = prop->len;
1309   BLO_read_data_address(reader, &prop->data.pointer);
1310 }
1311 
IDP_DirectLinkGroup(IDProperty * prop,BlendDataReader * reader)1312 static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader)
1313 {
1314   ListBase *lb = &prop->data.group;
1315   IDProperty *loop;
1316 
1317   BLO_read_list(reader, lb);
1318 
1319   /*Link child id properties now*/
1320   for (loop = prop->data.group.first; loop; loop = loop->next) {
1321     IDP_DirectLinkProperty(loop, reader);
1322   }
1323 }
1324 
IDP_DirectLinkProperty(IDProperty * prop,BlendDataReader * reader)1325 static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader)
1326 {
1327   switch (prop->type) {
1328     case IDP_GROUP:
1329       IDP_DirectLinkGroup(prop, reader);
1330       break;
1331     case IDP_STRING:
1332       IDP_DirectLinkString(prop, reader);
1333       break;
1334     case IDP_ARRAY:
1335       IDP_DirectLinkArray(prop, reader);
1336       break;
1337     case IDP_IDPARRAY:
1338       IDP_DirectLinkIDPArray(prop, reader);
1339       break;
1340     case IDP_DOUBLE:
1341       /* Workaround for doubles.
1342        * They are stored in the same field as `int val, val2` in the #IDPropertyData struct,
1343        * they have to deal with endianness specifically.
1344        *
1345        * In theory, val and val2 would've already been swapped
1346        * if switch_endian is true, so we have to first un-swap
1347        * them then re-swap them as a single 64-bit entity. */
1348       if (BLO_read_requires_endian_switch(reader)) {
1349         BLI_endian_switch_int32(&prop->data.val);
1350         BLI_endian_switch_int32(&prop->data.val2);
1351         BLI_endian_switch_int64((int64_t *)&prop->data.val);
1352       }
1353       break;
1354     case IDP_INT:
1355     case IDP_FLOAT:
1356     case IDP_ID:
1357       break; /* Nothing special to do here. */
1358     default:
1359       /* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code,
1360        * IDP are way too polymorphic to do it safely. */
1361       printf(
1362           "%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type);
1363       /* Note: we do not attempt to free unknown prop, we have no way to know how to do that! */
1364       prop->type = IDP_INT;
1365       prop->subtype = 0;
1366       IDP_Int(prop) = 0;
1367   }
1368 }
1369 
IDP_BlendReadData_impl(BlendDataReader * reader,IDProperty ** prop,const char * caller_func_id)1370 void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const char *caller_func_id)
1371 {
1372   if (*prop) {
1373     if ((*prop)->type == IDP_GROUP) {
1374       IDP_DirectLinkGroup(*prop, reader);
1375     }
1376     else {
1377       /* corrupt file! */
1378       printf("%s: found non group data, freeing type %d!\n", caller_func_id, (*prop)->type);
1379       /* don't risk id, data's likely corrupt. */
1380       // IDP_FreePropertyContent(*prop);
1381       *prop = NULL;
1382     }
1383   }
1384 }
1385 
IDP_BlendReadLib(BlendLibReader * reader,IDProperty * prop)1386 void IDP_BlendReadLib(BlendLibReader *reader, IDProperty *prop)
1387 {
1388   if (!prop) {
1389     return;
1390   }
1391 
1392   switch (prop->type) {
1393     case IDP_ID: /* PointerProperty */
1394     {
1395       void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop));
1396       if (IDP_Id(prop) && !newaddr && G.debug) {
1397         printf("Error while loading \"%s\". Data not found in file!\n", prop->name);
1398       }
1399       prop->data.pointer = newaddr;
1400       break;
1401     }
1402     case IDP_IDPARRAY: /* CollectionProperty */
1403     {
1404       IDProperty *idp_array = IDP_IDPArray(prop);
1405       for (int i = 0; i < prop->len; i++) {
1406         IDP_BlendReadLib(reader, &(idp_array[i]));
1407       }
1408       break;
1409     }
1410     case IDP_GROUP: /* PointerProperty */
1411     {
1412       LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
1413         IDP_BlendReadLib(reader, loop);
1414       }
1415       break;
1416     }
1417     default:
1418       break; /* Nothing to do for other IDProps. */
1419   }
1420 }
1421 
IDP_BlendReadExpand(struct BlendExpander * expander,IDProperty * prop)1422 void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop)
1423 {
1424   if (!prop) {
1425     return;
1426   }
1427 
1428   switch (prop->type) {
1429     case IDP_ID:
1430       BLO_expand(expander, IDP_Id(prop));
1431       break;
1432     case IDP_IDPARRAY: {
1433       IDProperty *idp_array = IDP_IDPArray(prop);
1434       for (int i = 0; i < prop->len; i++) {
1435         IDP_BlendReadExpand(expander, &idp_array[i]);
1436       }
1437       break;
1438     }
1439     case IDP_GROUP:
1440       LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
1441         IDP_BlendReadExpand(expander, loop);
1442       }
1443       break;
1444   }
1445 }
1446 
1447 /** \} */
1448