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