Cleanup: Split some code out of rna_access.c
[blender.git] / source / blender / makesrna / intern / rna_access_compare_override.c
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
17 /** \file
18  * \ingroup RNA
19  */
20
21 #include "MEM_guardedalloc.h"
22
23 #include "DNA_ID.h"
24 #include "DNA_constraint_types.h"
25 #include "DNA_modifier_types.h"
26
27 #include "BLI_utildefines.h"
28 #include "BLI_string.h"
29
30 #ifdef DEBUG_OVERRIDE_TIMEIT
31 #  include "PIL_time_utildefines.h"
32 #endif
33
34 #include "BKE_idprop.h"
35 #include "BKE_library_override.h"
36 #include "BKE_main.h"
37
38 #include "RNA_access.h"
39 #include "RNA_define.h"
40 #include "RNA_enum_types.h"
41
42 #include "rna_internal.h"
43 #include "rna_access_internal.h"
44
45 int RNA_property_override_flag(PropertyRNA *prop)
46 {
47   return rna_ensure_property(prop)->flag_override;
48 }
49
50 /** \note Does not take into account editable status, this has to be checked separately
51  * (using #RNA_property_editable_flag() usually). */
52 bool RNA_property_overridable_get(PointerRNA *ptr, PropertyRNA *prop)
53 {
54   if (prop->magic == RNA_MAGIC) {
55     /* Special handling for insertions of constraints or modifiers... */
56     /* TODO Note We may want to add a more generic system to RNA
57      * (like a special property in struct of items)
58      * if we get more overrideable collections,
59      * for now we can live with those special-cases handling I think. */
60     if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
61       bConstraint *con = ptr->data;
62       if (con->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) {
63         return true;
64       }
65     }
66     else if (RNA_struct_is_a(ptr->type, &RNA_Modifier)) {
67       ModifierData *mod = ptr->data;
68       if (mod->flag & eModifierFlag_OverrideLibrary_Local) {
69         return true;
70       }
71     }
72     /* If this is a RNA-defined property (real or 'virtual' IDProp),
73      * we want to use RNA prop flag. */
74     return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON) &&
75            (prop->flag_override & PROPOVERRIDE_OVERRIDABLE_LIBRARY);
76   }
77   else {
78     /* If this is a real 'pure' IDProp (aka custom property), we want to use the IDProp flag. */
79     return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON) &&
80            (((IDProperty *)prop)->flag & IDP_FLAG_OVERRIDABLE_LIBRARY);
81   }
82 }
83
84 /* Should only be used for custom properties */
85 bool RNA_property_overridable_library_set(PointerRNA *UNUSED(ptr),
86                                           PropertyRNA *prop,
87                                           const bool is_overridable)
88 {
89   /* Only works for pure custom properties IDProps. */
90   if (prop->magic != RNA_MAGIC) {
91     IDProperty *idprop = (IDProperty *)prop;
92
93     idprop->flag = is_overridable ? (idprop->flag | IDP_FLAG_OVERRIDABLE_LIBRARY) :
94                                     (idprop->flag & ~IDP_FLAG_OVERRIDABLE_LIBRARY);
95     return true;
96   }
97
98   return false;
99 }
100
101 bool RNA_property_overridden(PointerRNA *ptr, PropertyRNA *prop)
102 {
103   char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
104   ID *id = ptr->id.data;
105
106   if (rna_path == NULL || id == NULL || id->override_library == NULL) {
107     return false;
108   }
109
110   return (BKE_override_library_property_find(id->override_library, rna_path) != NULL);
111 }
112
113 bool RNA_property_comparable(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
114 {
115   prop = rna_ensure_property(prop);
116
117   return !(prop->flag_override & PROPOVERRIDE_NO_COMPARISON);
118 }
119
120 static bool rna_property_override_operation_apply(Main *bmain,
121                                                   PointerRNA *ptr_local,
122                                                   PointerRNA *ptr_override,
123                                                   PointerRNA *ptr_storage,
124                                                   PropertyRNA *prop_local,
125                                                   PropertyRNA *prop_override,
126                                                   PropertyRNA *prop_storage,
127                                                   PointerRNA *ptr_item_local,
128                                                   PointerRNA *ptr_item_override,
129                                                   PointerRNA *ptr_item_storage,
130                                                   IDOverrideLibraryPropertyOperation *opop);
131
132 bool RNA_property_copy(
133     Main *bmain, PointerRNA *ptr, PointerRNA *fromptr, PropertyRNA *prop, int index)
134 {
135   if (!RNA_property_editable(ptr, prop)) {
136     return false;
137   }
138
139   PropertyRNA *prop_dst = prop;
140   PropertyRNA *prop_src = prop;
141
142   /* Ensure we get real property data,
143    * be it an actual RNA property, or an IDProperty in disguise. */
144   prop_dst = rna_ensure_property_realdata(&prop_dst, ptr);
145   prop_src = rna_ensure_property_realdata(&prop_src, fromptr);
146
147   /* IDprops: destination may not exist, if source does and is set, try to create it. */
148   /* Note: this is sort of quick hack/bandage to fix the issue,
149    * we need to rethink how IDProps are handled in 'diff' RNA code completely, imho... */
150   if (prop_src != NULL && prop_dst == NULL && RNA_property_is_set(fromptr, prop)) {
151     BLI_assert(prop_src->magic != RNA_MAGIC);
152     IDProperty *idp_dst = RNA_struct_idprops(ptr, true);
153     IDProperty *prop_idp_dst = IDP_CopyProperty((IDProperty *)prop_src);
154     IDP_AddToGroup(idp_dst, prop_idp_dst);
155     rna_idproperty_touch(prop_idp_dst);
156     /* Nothing else to do here... */
157     return true;
158   }
159
160   if (ELEM(NULL, prop_dst, prop_src)) {
161     return false;
162   }
163
164   IDOverrideLibraryPropertyOperation opop = {
165       .operation = IDOVERRIDE_LIBRARY_OP_REPLACE,
166       .subitem_reference_index = index,
167       .subitem_local_index = index,
168   };
169   return rna_property_override_operation_apply(
170       bmain, ptr, fromptr, NULL, prop_dst, prop_src, NULL, NULL, NULL, NULL, &opop);
171 }
172
173 static int rna_property_override_diff(Main *bmain,
174                                       PointerRNA *ptr_a,
175                                       PointerRNA *ptr_b,
176                                       PropertyRNA *prop,
177                                       PropertyRNA *prop_a,
178                                       PropertyRNA *prop_b,
179                                       const char *rna_path,
180                                       eRNACompareMode mode,
181                                       IDOverrideLibrary *override,
182                                       const int flags,
183                                       eRNAOverrideMatchResult *r_report_flags);
184
185 bool RNA_property_equals(
186     Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop, eRNACompareMode mode)
187 {
188   BLI_assert(ELEM(mode, RNA_EQ_STRICT, RNA_EQ_UNSET_MATCH_ANY, RNA_EQ_UNSET_MATCH_NONE));
189
190   return (rna_property_override_diff(
191               bmain, ptr_a, ptr_b, prop, NULL, NULL, NULL, mode, NULL, 0, NULL) == 0);
192 }
193
194 bool RNA_struct_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACompareMode mode)
195 {
196   CollectionPropertyIterator iter;
197   PropertyRNA *iterprop;
198   bool equals = true;
199
200   if (ptr_a == NULL && ptr_b == NULL) {
201     return true;
202   }
203   else if (ptr_a == NULL || ptr_b == NULL) {
204     return false;
205   }
206   else if (ptr_a->type != ptr_b->type) {
207     return false;
208   }
209
210   iterprop = RNA_struct_iterator_property(ptr_a->type);
211
212   RNA_property_collection_begin(ptr_a, iterprop, &iter);
213   for (; iter.valid; RNA_property_collection_next(&iter)) {
214     PropertyRNA *prop = iter.ptr.data;
215
216     if (!RNA_property_equals(bmain, ptr_a, ptr_b, prop, mode)) {
217       equals = false;
218       break;
219     }
220   }
221   RNA_property_collection_end(&iter);
222
223   return equals;
224 }
225
226 /* Low-level functions, also used by non-override RNA API like copy or equality check. */
227
228 /** Generic RNA property diff function.
229  *
230  * \note about \a prop and \a prop_a/prop_b parameters:
231  * the former is expected to be an 'un-resolved' one,
232  * while the two later are expected to be fully resolved ones
233  * (i.e. to be the IDProps when they should be, etc.).
234  * When \a prop is given, \a prop_a and \a prop_b should always be NULL, and vice-versa.
235  * This is necessary, because we cannot perform 'set/unset' checks on resolved properties
236  * (unset IDProps would merely be NULL then).
237  *
238  * \note When there is no equality,
239  * but we cannot determine an order (greater than/lesser than), we return 1.
240  */
241 static int rna_property_override_diff(Main *bmain,
242                                       PointerRNA *ptr_a,
243                                       PointerRNA *ptr_b,
244                                       PropertyRNA *prop,
245                                       PropertyRNA *prop_a,
246                                       PropertyRNA *prop_b,
247                                       const char *rna_path,
248                                       eRNACompareMode mode,
249                                       IDOverrideLibrary *override,
250                                       const int flags,
251                                       eRNAOverrideMatchResult *r_report_flags)
252 {
253   if (prop != NULL) {
254     BLI_assert(prop_a == NULL && prop_b == NULL);
255     prop_a = prop;
256     prop_b = prop;
257   }
258
259   if (ELEM(NULL, prop_a, prop_b)) {
260     return (prop_a == prop_b) ? 0 : 1;
261   }
262
263   if (!RNA_property_comparable(ptr_a, prop_a) || !RNA_property_comparable(ptr_b, prop_b)) {
264     return 0;
265   }
266
267   if (mode == RNA_EQ_UNSET_MATCH_ANY) {
268     /* uninitialized properties are assumed to match anything */
269     if (!RNA_property_is_set(ptr_a, prop_a) || !RNA_property_is_set(ptr_b, prop_b)) {
270       return 0;
271     }
272   }
273   else if (mode == RNA_EQ_UNSET_MATCH_NONE) {
274     /* unset properties never match set properties */
275     if (RNA_property_is_set(ptr_a, prop_a) != RNA_property_is_set(ptr_b, prop_b)) {
276       return 1;
277     }
278   }
279
280   if (prop != NULL) {
281     /* Ensure we get real property data, be it an actual RNA property,
282      * or an IDProperty in disguise. */
283     prop_a = rna_ensure_property_realdata(&prop_a, ptr_a);
284     prop_b = rna_ensure_property_realdata(&prop_b, ptr_b);
285
286     if (ELEM(NULL, prop_a, prop_b)) {
287       return (prop_a == prop_b) ? 0 : 1;
288     }
289   }
290
291   /* Check if we are working with arrays. */
292   const bool is_array_a = RNA_property_array_check(prop_a);
293   const bool is_array_b = RNA_property_array_check(prop_b);
294
295   if (is_array_a != is_array_b) {
296     /* Should probably never happen actually... */
297     BLI_assert(0);
298     return is_array_a ? 1 : -1;
299   }
300
301   /* Get the length of the array to work with. */
302   const int len_a = RNA_property_array_length(ptr_a, prop_a);
303   const int len_b = RNA_property_array_length(ptr_b, prop_b);
304
305   if (len_a != len_b) {
306     /* Do not handle override in that case,
307      * we do not support insertion/deletion from arrays for now. */
308     return len_a > len_b ? 1 : -1;
309   }
310
311   if (is_array_a && len_a == 0) {
312     /* Empty arrays, will happen in some case with dynamic ones. */
313     return 0;
314   }
315
316   RNAPropOverrideDiff override_diff = NULL;
317   /* Special case for IDProps, we use default callback then. */
318   if (prop_a->magic != RNA_MAGIC) {
319     override_diff = rna_property_override_diff_default;
320     if (prop_b->magic == RNA_MAGIC && prop_b->override_diff != override_diff) {
321       override_diff = NULL;
322     }
323   }
324   else if (prop_b->magic != RNA_MAGIC) {
325     override_diff = rna_property_override_diff_default;
326     if (prop_a->override_diff != override_diff) {
327       override_diff = NULL;
328     }
329   }
330   else if (prop_a->override_diff == prop_b->override_diff) {
331     override_diff = prop_a->override_diff;
332   }
333
334   if (override_diff == NULL) {
335 #ifndef NDEBUG
336     printf("'%s' gives unmatching or NULL RNA diff callbacks, should not happen (%d vs. %d).\n",
337            rna_path ?
338                rna_path :
339                (prop_a->magic != RNA_MAGIC ? ((IDProperty *)prop_a)->name : prop_a->identifier),
340            prop_a->magic == RNA_MAGIC,
341            prop_b->magic == RNA_MAGIC);
342 #endif
343     BLI_assert(0);
344     return 1;
345   }
346
347   bool override_changed = false;
348   int diff_flags = flags;
349   if (!RNA_property_overridable_get(ptr_a, prop_a)) {
350     diff_flags &= ~RNA_OVERRIDE_COMPARE_CREATE;
351   }
352   const int diff = override_diff(bmain,
353                                  ptr_a,
354                                  ptr_b,
355                                  prop_a,
356                                  prop_b,
357                                  len_a,
358                                  len_b,
359                                  mode,
360                                  override,
361                                  rna_path,
362                                  diff_flags,
363                                  &override_changed);
364   if (override_changed && r_report_flags) {
365     *r_report_flags |= RNA_OVERRIDE_MATCH_RESULT_CREATED;
366   }
367
368   return diff;
369 }
370
371 /* Modify local data-block to make it ready for override application
372  * (only needed for diff operations, where we use
373  * the local data-block's data as second operand). */
374 static bool rna_property_override_operation_store(Main *bmain,
375                                                   PointerRNA *ptr_local,
376                                                   PointerRNA *ptr_reference,
377                                                   PointerRNA *ptr_storage,
378                                                   PropertyRNA *prop_local,
379                                                   PropertyRNA *prop_reference,
380                                                   PropertyRNA *prop_storage,
381                                                   IDOverrideLibraryProperty *op)
382 {
383   int len_local, len_reference, len_storage = 0;
384   bool changed = false;
385
386   if (ptr_storage == NULL) {
387     return changed;
388   }
389
390   /* get the length of the array to work with */
391   len_local = RNA_property_array_length(ptr_local, prop_local);
392   len_reference = RNA_property_array_length(ptr_reference, prop_reference);
393   if (prop_storage) {
394     len_storage = RNA_property_array_length(ptr_storage, prop_storage);
395   }
396
397   if (len_local != len_reference || len_local != len_storage) {
398     /* Do not handle override in that case,
399      * we do not support insertion/deletion from arrays for now. */
400     return changed;
401   }
402
403   BLI_assert(prop_local->override_store == prop_reference->override_store &&
404              (!ptr_storage || prop_local->override_store == prop_storage->override_store) &&
405              prop_local->override_store != NULL);
406
407   for (IDOverrideLibraryPropertyOperation *opop = op->operations.first; opop; opop = opop->next) {
408     /* Only needed for diff operations. */
409     if (!ELEM(opop->operation,
410               IDOVERRIDE_LIBRARY_OP_ADD,
411               IDOVERRIDE_LIBRARY_OP_SUBTRACT,
412               IDOVERRIDE_LIBRARY_OP_MULTIPLY)) {
413       continue;
414     }
415
416     if (prop_local->override_store(bmain,
417                                    ptr_local,
418                                    ptr_reference,
419                                    ptr_storage,
420                                    prop_local,
421                                    prop_reference,
422                                    prop_storage,
423                                    len_local,
424                                    len_reference,
425                                    len_storage,
426                                    opop)) {
427       changed = true;
428     }
429   }
430
431   return changed;
432 }
433
434 static bool rna_property_override_operation_apply(Main *bmain,
435                                                   PointerRNA *ptr_dst,
436                                                   PointerRNA *ptr_src,
437                                                   PointerRNA *ptr_storage,
438                                                   PropertyRNA *prop_dst,
439                                                   PropertyRNA *prop_src,
440                                                   PropertyRNA *prop_storage,
441                                                   PointerRNA *ptr_item_dst,
442                                                   PointerRNA *ptr_item_src,
443                                                   PointerRNA *ptr_item_storage,
444                                                   IDOverrideLibraryPropertyOperation *opop)
445 {
446   int len_dst, len_src, len_storage = 0;
447
448   const short override_op = opop->operation;
449
450   if (override_op == IDOVERRIDE_LIBRARY_OP_NOOP) {
451     return true;
452   }
453
454   if (ELEM(override_op,
455            IDOVERRIDE_LIBRARY_OP_ADD,
456            IDOVERRIDE_LIBRARY_OP_SUBTRACT,
457            IDOVERRIDE_LIBRARY_OP_MULTIPLY) &&
458       !ptr_storage) {
459     /* We cannot apply 'diff' override operations without some reference storage.
460      * This should typically only happen at read time of .blend file... */
461     return false;
462   }
463
464   if (ELEM(override_op,
465            IDOVERRIDE_LIBRARY_OP_ADD,
466            IDOVERRIDE_LIBRARY_OP_SUBTRACT,
467            IDOVERRIDE_LIBRARY_OP_MULTIPLY) &&
468       !prop_storage) {
469     /* We cannot apply 'diff' override operations without some reference storage.
470      * This should typically only happen at read time of .blend file... */
471     return false;
472   }
473
474   RNAPropOverrideApply override_apply = NULL;
475   /* Special case for IDProps, we use default callback then. */
476   if (prop_dst->magic != RNA_MAGIC) {
477     override_apply = rna_property_override_apply_default;
478     if (prop_src->magic == RNA_MAGIC && prop_src->override_apply != override_apply) {
479       override_apply = NULL;
480     }
481   }
482   else if (prop_src->magic != RNA_MAGIC) {
483     override_apply = rna_property_override_apply_default;
484     if (prop_dst->override_apply != override_apply) {
485       override_apply = NULL;
486     }
487   }
488   else if (prop_dst->override_apply == prop_src->override_apply) {
489     override_apply = prop_dst->override_apply;
490   }
491
492   if (ptr_storage && prop_storage->magic == RNA_MAGIC &&
493       prop_storage->override_apply != override_apply) {
494     override_apply = NULL;
495   }
496
497   if (override_apply == NULL) {
498 #ifndef NDEBUG
499     printf("'%s' gives unmatching or NULL RNA copy callbacks, should not happen (%d vs. %d).\n",
500            prop_dst->magic != RNA_MAGIC ? ((IDProperty *)prop_dst)->name : prop_dst->identifier,
501            prop_dst->magic == RNA_MAGIC,
502            prop_src->magic == RNA_MAGIC);
503 #endif
504     BLI_assert(0);
505     return false;
506   }
507
508   /* get the length of the array to work with */
509   len_dst = RNA_property_array_length(ptr_dst, prop_dst);
510   len_src = RNA_property_array_length(ptr_src, prop_src);
511   if (ptr_storage) {
512     len_storage = RNA_property_array_length(ptr_storage, prop_storage);
513   }
514
515   if (len_dst != len_src || (ptr_storage && len_dst != len_storage)) {
516     /* Do not handle override in that case,
517      * we do not support insertion/deletion from arrays for now. */
518     return false;
519   }
520
521   /* get and set the default values as appropriate for the various types */
522   return override_apply(bmain,
523                         ptr_dst,
524                         ptr_src,
525                         ptr_storage,
526                         prop_dst,
527                         prop_src,
528                         prop_storage,
529                         len_dst,
530                         len_src,
531                         len_storage,
532                         ptr_item_dst,
533                         ptr_item_src,
534                         ptr_item_storage,
535                         opop);
536 }
537
538 /**
539  * Check whether reference and local overridden data match (are the same),
540  * with respect to given restrictive sets of properties.
541  * If requested, will generate needed new property overrides, and/or restore values from reference.
542  *
543  * \param r_report_flags: If given,
544  * will be set with flags matching actions taken by the function on \a ptr_local.
545  *
546  * \return True if _resulting_ \a ptr_local does match \a ptr_reference.
547  */
548 bool RNA_struct_override_matches(Main *bmain,
549                                  PointerRNA *ptr_local,
550                                  PointerRNA *ptr_reference,
551                                  const char *root_path,
552                                  IDOverrideLibrary *override,
553                                  const eRNAOverrideMatch flags,
554                                  eRNAOverrideMatchResult *r_report_flags)
555 {
556   CollectionPropertyIterator iter;
557   PropertyRNA *iterprop;
558   bool matching = true;
559
560   BLI_assert(ptr_local->type == ptr_reference->type);
561   BLI_assert(ptr_local->id.data && ptr_reference->id.data);
562
563   const bool ignore_non_overridable = (flags & RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE) != 0;
564   const bool ignore_overridden = (flags & RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN) != 0;
565   const bool do_create = (flags & RNA_OVERRIDE_COMPARE_CREATE) != 0;
566   const bool do_restore = (flags & RNA_OVERRIDE_COMPARE_RESTORE) != 0;
567
568 //#define DEBUG_OVERRIDE_TIMEIT
569 #ifdef DEBUG_OVERRIDE_TIMEIT
570   static float _sum_time_global = 0.0f;
571   static float _num_time_global = 0.0f;
572   double _timeit_time_global;
573   static float _sum_time_diffing = 0.0f;
574   static float _delta_time_diffing = 0.0f;
575   static int _num_delta_time_diffing = 0.0f;
576   static float _num_time_diffing = 0.0f;
577   double _timeit_time_diffing;
578
579   if (!root_path) {
580     _delta_time_diffing = 0.0f;
581     _num_delta_time_diffing = 0;
582     _timeit_time_global = PIL_check_seconds_timer();
583   }
584 #endif
585
586   iterprop = RNA_struct_iterator_property(ptr_local->type);
587
588   for (RNA_property_collection_begin(ptr_local, iterprop, &iter); iter.valid;
589        RNA_property_collection_next(&iter)) {
590     PropertyRNA *prop_local = iter.ptr.data;
591     PropertyRNA *prop_reference = iter.ptr.data;
592
593     /* Ensure we get real property data, be it an actual RNA property,
594      * or an IDProperty in disguise. */
595     prop_local = rna_ensure_property_realdata(&prop_local, ptr_local);
596     prop_reference = rna_ensure_property_realdata(&prop_reference, ptr_reference);
597
598     if (ELEM(NULL, prop_local, prop_reference)) {
599       continue;
600     }
601
602     if (ignore_non_overridable && !RNA_property_overridable_get(ptr_local, prop_local)) {
603       continue;
604     }
605
606 #if 0 /* This actually makes things slower, since it has to check for animation paths etc! */
607     if (RNA_property_animated(ptr_local, prop_local)) {
608       /* We cannot do anything here really, animation is some kind of dynamic overrides that has
609        * precedence over static one... */
610       continue;
611     }
612 #endif
613
614 #define RNA_PATH_BUFFSIZE 8192
615 #define RNA_PATH_PRINTF(_str, ...) \
616   if (BLI_snprintf(rna_path, RNA_PATH_BUFFSIZE, (_str), __VA_ARGS__) >= RNA_PATH_BUFFSIZE) { \
617     rna_path = BLI_sprintfN((_str), __VA_ARGS__); \
618   } \
619   (void)0
620 #define RNA_PATH_FREE \
621   if (rna_path != rna_path_buffer) \
622   MEM_freeN(rna_path)
623
624     char rna_path_buffer[RNA_PATH_BUFFSIZE];
625     char *rna_path = rna_path_buffer;
626
627     /* XXX TODO this will have to be refined to handle collections insertions, and array items */
628     if (root_path) {
629       /* Inlined building, much much more efficient. */
630       if (prop_local->magic == RNA_MAGIC) {
631         RNA_PATH_PRINTF("%s.%s", root_path, RNA_property_identifier(prop_local));
632       }
633       else {
634         RNA_PATH_PRINTF("%s[\"%s\"]", root_path, RNA_property_identifier(prop_local));
635       }
636     }
637     else {
638       /* This is rather slow, but is not much called, so not really worth optimizing. */
639       rna_path = RNA_path_from_ID_to_property(ptr_local, prop_local);
640     }
641     if (rna_path == NULL) {
642       continue;
643     }
644
645     //    printf("Override Checking %s\n", rna_path);
646
647     if (ignore_overridden && BKE_override_library_property_find(override, rna_path) != NULL) {
648       RNA_PATH_FREE;
649       continue;
650     }
651
652 #ifdef DEBUG_OVERRIDE_TIMEIT
653     if (!root_path) {
654       _timeit_time_diffing = PIL_check_seconds_timer();
655     }
656 #endif
657
658     eRNAOverrideMatchResult report_flags = 0;
659     const int diff = rna_property_override_diff(bmain,
660                                                 ptr_local,
661                                                 ptr_reference,
662                                                 NULL,
663                                                 prop_local,
664                                                 prop_reference,
665                                                 rna_path,
666                                                 RNA_EQ_STRICT,
667                                                 override,
668                                                 flags,
669                                                 &report_flags);
670
671 #ifdef DEBUG_OVERRIDE_TIMEIT
672     if (!root_path) {
673       const float _delta_time = (float)(PIL_check_seconds_timer() - _timeit_time_diffing);
674       _delta_time_diffing += _delta_time;
675       _num_delta_time_diffing++;
676     }
677 #endif
678
679     matching = matching && diff == 0;
680     if (r_report_flags) {
681       *r_report_flags |= report_flags;
682     }
683
684     if (diff != 0) {
685       /* XXX TODO: refine this for per-item overriding of arrays... */
686       IDOverrideLibraryProperty *op = BKE_override_library_property_find(override, rna_path);
687       IDOverrideLibraryPropertyOperation *opop = op ? op->operations.first : NULL;
688
689       if (do_restore && (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) == 0) {
690         /* We are allowed to restore to reference's values. */
691         if (ELEM(NULL, op, opop) || opop->operation == IDOVERRIDE_LIBRARY_OP_NOOP) {
692           /* We should restore that property to its reference value */
693           if (RNA_property_editable(ptr_local, prop_local)) {
694             IDOverrideLibraryPropertyOperation opop_tmp = {
695                 .operation = IDOVERRIDE_LIBRARY_OP_REPLACE,
696                 .subitem_reference_index = -1,
697                 .subitem_local_index = -1,
698             };
699             rna_property_override_operation_apply(bmain,
700                                                   ptr_local,
701                                                   ptr_reference,
702                                                   NULL,
703                                                   prop_local,
704                                                   prop_reference,
705                                                   NULL,
706                                                   NULL,
707                                                   NULL,
708                                                   NULL,
709                                                   &opop_tmp);
710             if (r_report_flags) {
711               *r_report_flags |= RNA_OVERRIDE_MATCH_RESULT_RESTORED;
712             }
713           }
714           else {
715             /* Too noisy for now, this triggers on runtime props like transform matrices etc. */
716 #if 0
717             BLI_assert(!"We have differences between reference and "
718                        "overriding data on non-editable property.");
719 #endif
720             matching = false;
721           }
722         }
723       }
724       else if ((report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) == 0 && ELEM(NULL, op, opop)) {
725         /* This property is not overridden, and differs from reference, so we have no match. */
726         matching = false;
727         if (!(do_create || do_restore)) {
728           /* Since we have no 'changing' action allowed, we can break here. */
729           MEM_SAFE_FREE(rna_path);
730           break;
731         }
732       }
733     }
734
735     RNA_PATH_FREE;
736
737 #undef RNA_PATH_BUFFSIZE
738 #undef RNA_PATH_PRINTF
739 #undef RNA_PATH_FREE
740   }
741   RNA_property_collection_end(&iter);
742
743 #ifdef DEBUG_OVERRIDE_TIMEIT
744   if (!root_path) {
745     const float _delta_time = (float)(PIL_check_seconds_timer() - _timeit_time_global);
746     _sum_time_global += _delta_time;
747     _num_time_global++;
748     _sum_time_diffing += _delta_time_diffing;
749     _num_time_diffing++;
750     printf("ID: %s\n", ((ID *)ptr_local->id.data)->name);
751     printf("time end      (%s): %.6f\n", __func__, _delta_time);
752     printf("time averaged (%s): %.6f (total: %.6f, in %d runs)\n",
753            __func__,
754            (_sum_time_global / _num_time_global),
755            _sum_time_global,
756            (int)_num_time_global);
757     printf("diffing time end      (%s): %.6f (in %d runs)\n",
758            __func__,
759            _delta_time_diffing,
760            _num_delta_time_diffing);
761     printf("diffing time averaged (%s): %.6f (total: %.6f, in %d runs)\n",
762            __func__,
763            (_sum_time_diffing / _num_time_diffing),
764            _sum_time_diffing,
765            (int)_num_time_diffing);
766   }
767 #endif
768
769   return matching;
770 }
771
772 /** Store needed second operands into \a storage data-block
773  * for differential override operations. */
774 bool RNA_struct_override_store(Main *bmain,
775                                PointerRNA *ptr_local,
776                                PointerRNA *ptr_reference,
777                                PointerRNA *ptr_storage,
778                                IDOverrideLibrary *override)
779 {
780   bool changed = false;
781
782 #ifdef DEBUG_OVERRIDE_TIMEIT
783   TIMEIT_START_AVERAGED(RNA_struct_override_store);
784 #endif
785   for (IDOverrideLibraryProperty *op = override->properties.first; op; op = op->next) {
786     /* Simplified for now! */
787     PointerRNA data_reference, data_local;
788     PropertyRNA *prop_reference, *prop_local;
789
790     if (RNA_path_resolve_property(ptr_local, op->rna_path, &data_local, &prop_local) &&
791         RNA_path_resolve_property(ptr_reference, op->rna_path, &data_reference, &prop_reference)) {
792       PointerRNA data_storage;
793       PropertyRNA *prop_storage = NULL;
794
795       /* It is totally OK if this does not success,
796        * only a subset of override operations actually need storage. */
797       if (ptr_storage && (ptr_storage->id.data != NULL)) {
798         RNA_path_resolve_property(ptr_storage, op->rna_path, &data_storage, &prop_storage);
799       }
800
801       if (rna_property_override_operation_store(bmain,
802                                                 &data_local,
803                                                 &data_reference,
804                                                 &data_storage,
805                                                 prop_reference,
806                                                 prop_local,
807                                                 prop_storage,
808                                                 op)) {
809         changed = true;
810       }
811     }
812   }
813 #ifdef DEBUG_OVERRIDE_TIMEIT
814   TIMEIT_END_AVERAGED(RNA_struct_override_store);
815 #endif
816
817   return changed;
818 }
819
820 static void rna_property_override_apply_ex(Main *bmain,
821                                            PointerRNA *ptr_dst,
822                                            PointerRNA *ptr_src,
823                                            PointerRNA *ptr_storage,
824                                            PropertyRNA *prop_dst,
825                                            PropertyRNA *prop_src,
826                                            PropertyRNA *prop_storage,
827                                            PointerRNA *ptr_item_dst,
828                                            PointerRNA *ptr_item_src,
829                                            PointerRNA *ptr_item_storage,
830                                            IDOverrideLibraryProperty *op,
831                                            const bool do_insert)
832 {
833   for (IDOverrideLibraryPropertyOperation *opop = op->operations.first; opop; opop = opop->next) {
834     if (!do_insert != !ELEM(opop->operation,
835                             IDOVERRIDE_LIBRARY_OP_INSERT_AFTER,
836                             IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE)) {
837       if (!do_insert) {
838         printf("Skipping insert override operations in first pass (%s)!\n", op->rna_path);
839       }
840       continue;
841     }
842
843     /* Note: will have to think about putting that logic into its own function maybe?
844      * Would be nice to have it in a single place...
845      * Note that here, src is the local saved ID, and dst is a copy of the linked ID (since we use
846      * local ID as storage to apply local changes on top of a clean copy of the linked data). */
847     PointerRNA private_ptr_item_dst, private_ptr_item_src, private_ptr_item_storage;
848     if (opop->subitem_local_name != NULL || opop->subitem_reference_name != NULL ||
849         opop->subitem_local_index != -1 || opop->subitem_reference_index != -1) {
850       RNA_POINTER_INVALIDATE(&private_ptr_item_dst);
851       RNA_POINTER_INVALIDATE(&private_ptr_item_src);
852       RNA_POINTER_INVALIDATE(&private_ptr_item_storage);
853       if (opop->subitem_local_name != NULL) {
854         RNA_property_collection_lookup_string(
855             ptr_src, prop_src, opop->subitem_local_name, &private_ptr_item_src);
856         if (opop->subitem_reference_name != NULL) {
857           RNA_property_collection_lookup_string(
858               ptr_dst, prop_dst, opop->subitem_reference_name, &private_ptr_item_dst);
859         }
860         else {
861           RNA_property_collection_lookup_string(
862               ptr_dst, prop_dst, opop->subitem_local_name, &private_ptr_item_dst);
863         }
864       }
865       else if (opop->subitem_reference_name != NULL) {
866         RNA_property_collection_lookup_string(
867             ptr_src, prop_src, opop->subitem_reference_name, &private_ptr_item_src);
868         RNA_property_collection_lookup_string(
869             ptr_dst, prop_dst, opop->subitem_reference_name, &private_ptr_item_dst);
870       }
871       else if (opop->subitem_local_index != -1) {
872         RNA_property_collection_lookup_int(
873             ptr_src, prop_src, opop->subitem_local_index, &private_ptr_item_src);
874         if (opop->subitem_reference_index != -1) {
875           RNA_property_collection_lookup_int(
876               ptr_dst, prop_dst, opop->subitem_reference_index, &private_ptr_item_dst);
877         }
878         else {
879           RNA_property_collection_lookup_int(
880               ptr_dst, prop_dst, opop->subitem_local_index, &private_ptr_item_dst);
881         }
882       }
883       else if (opop->subitem_reference_index != -1) {
884         RNA_property_collection_lookup_int(
885             ptr_src, prop_src, opop->subitem_reference_index, &private_ptr_item_src);
886         RNA_property_collection_lookup_int(
887             ptr_dst, prop_dst, opop->subitem_reference_index, &private_ptr_item_dst);
888       }
889       if (prop_storage != NULL) {
890         if (opop->subitem_local_name != NULL) {
891           RNA_property_collection_lookup_string(
892               ptr_storage, prop_storage, opop->subitem_local_name, &private_ptr_item_storage);
893         }
894         else if (opop->subitem_reference_name != NULL) {
895           RNA_property_collection_lookup_string(
896               ptr_storage, prop_storage, opop->subitem_reference_name, &private_ptr_item_storage);
897         }
898         else if (opop->subitem_local_index != -1) {
899           RNA_property_collection_lookup_int(
900               ptr_storage, prop_storage, opop->subitem_local_index, &private_ptr_item_storage);
901         }
902         else if (opop->subitem_reference_index != -1) {
903           RNA_property_collection_lookup_int(
904               ptr_storage, prop_storage, opop->subitem_reference_index, &private_ptr_item_storage);
905         }
906       }
907       ptr_item_dst = &private_ptr_item_dst;
908       ptr_item_src = &private_ptr_item_src;
909       ptr_item_storage = &private_ptr_item_storage;
910     }
911
912     if (!rna_property_override_operation_apply(bmain,
913                                                ptr_dst,
914                                                ptr_src,
915                                                ptr_storage,
916                                                prop_dst,
917                                                prop_src,
918                                                prop_storage,
919                                                ptr_item_dst,
920                                                ptr_item_src,
921                                                ptr_item_storage,
922                                                opop)) {
923       /* TODO No assert here, would be much much better to just report as warning,
924        * failing override applications will probably be fairly common! */
925       BLI_assert(0);
926     }
927   }
928 }
929
930 /** Apply given \a override operations on \a ptr_dst, using \a ptr_src
931  * (and \a ptr_storage for differential ops) as source. */
932 void RNA_struct_override_apply(Main *bmain,
933                                PointerRNA *ptr_dst,
934                                PointerRNA *ptr_src,
935                                PointerRNA *ptr_storage,
936                                IDOverrideLibrary *override)
937 {
938 #ifdef DEBUG_OVERRIDE_TIMEIT
939   TIMEIT_START_AVERAGED(RNA_struct_override_apply);
940 #endif
941   /* Note: Applying insert operations in a separate pass is mandatory.
942    * We could optimize this later, but for now, as inefficient as it is,
943    * don't think this is a critical point.
944    */
945   bool do_insert = false;
946   for (int i = 0; i < 2; i++, do_insert = true) {
947     for (IDOverrideLibraryProperty *op = override->properties.first; op; op = op->next) {
948       /* Simplified for now! */
949       PointerRNA data_src, data_dst;
950       PointerRNA data_item_src, data_item_dst;
951       PropertyRNA *prop_src, *prop_dst;
952
953       if (RNA_path_resolve_property_and_item_pointer(
954               ptr_dst, op->rna_path, &data_dst, &prop_dst, &data_item_dst) &&
955           RNA_path_resolve_property_and_item_pointer(
956               ptr_src, op->rna_path, &data_src, &prop_src, &data_item_src)) {
957         PointerRNA data_storage, data_item_storage;
958         PropertyRNA *prop_storage = NULL;
959
960         /* It is totally OK if this does not success,
961          * only a subset of override operations actually need storage. */
962         if (ptr_storage && (ptr_storage->id.data != NULL)) {
963           RNA_path_resolve_property_and_item_pointer(
964               ptr_storage, op->rna_path, &data_storage, &prop_storage, &data_item_storage);
965         }
966
967         rna_property_override_apply_ex(bmain,
968                                        &data_dst,
969                                        &data_src,
970                                        prop_storage ? &data_storage : NULL,
971                                        prop_dst,
972                                        prop_src,
973                                        prop_storage,
974                                        &data_item_dst,
975                                        &data_item_src,
976                                        prop_storage ? &data_item_storage : NULL,
977                                        op,
978                                        do_insert);
979       }
980 #ifndef NDEBUG
981       else {
982         printf(
983             "Failed to apply library override operation to '%s.%s' "
984             "(could not resolve some properties, local:  %d, override: %d)\n",
985             ((ID *)ptr_src->id.data)->name,
986             op->rna_path,
987             RNA_path_resolve_property(ptr_dst, op->rna_path, &data_dst, &prop_dst),
988             RNA_path_resolve_property(ptr_src, op->rna_path, &data_src, &prop_src));
989       }
990 #endif
991     }
992   }
993 #ifdef DEBUG_OVERRIDE_TIMEIT
994   TIMEIT_END_AVERAGED(RNA_struct_override_apply);
995 #endif
996 }
997
998 IDOverrideLibraryProperty *RNA_property_override_property_find(PointerRNA *ptr, PropertyRNA *prop)
999 {
1000   ID *id = ptr->id.data;
1001
1002   if (!id || !id->override_library) {
1003     return NULL;
1004   }
1005
1006   char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
1007   if (rna_path) {
1008     IDOverrideLibraryProperty *op = BKE_override_library_property_find(id->override_library,
1009                                                                        rna_path);
1010     MEM_freeN(rna_path);
1011     return op;
1012   }
1013   return NULL;
1014 }
1015
1016 IDOverrideLibraryProperty *RNA_property_override_property_get(PointerRNA *ptr,
1017                                                               PropertyRNA *prop,
1018                                                               bool *r_created)
1019 {
1020   ID *id = ptr->id.data;
1021
1022   if (!id || !id->override_library) {
1023     return NULL;
1024   }
1025
1026   char *rna_path = RNA_path_from_ID_to_property(ptr, prop);
1027   if (rna_path) {
1028     IDOverrideLibraryProperty *op = BKE_override_library_property_get(
1029         id->override_library, rna_path, r_created);
1030     MEM_freeN(rna_path);
1031     return op;
1032   }
1033   return NULL;
1034 }
1035
1036 IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_find(
1037     PointerRNA *ptr, PropertyRNA *prop, const int index, const bool strict, bool *r_strict)
1038 {
1039   IDOverrideLibraryProperty *op = RNA_property_override_property_find(ptr, prop);
1040
1041   if (!op) {
1042     return NULL;
1043   }
1044
1045   return BKE_override_library_property_operation_find(
1046       op, NULL, NULL, index, index, strict, r_strict);
1047 }
1048
1049 IDOverrideLibraryPropertyOperation *RNA_property_override_property_operation_get(
1050     PointerRNA *ptr,
1051     PropertyRNA *prop,
1052     const short operation,
1053     const int index,
1054     const bool strict,
1055     bool *r_strict,
1056     bool *r_created)
1057 {
1058   IDOverrideLibraryProperty *op = RNA_property_override_property_get(ptr, prop, NULL);
1059
1060   if (!op) {
1061     return NULL;
1062   }
1063
1064   return BKE_override_library_property_operation_get(
1065       op, operation, NULL, NULL, index, index, strict, r_strict, r_created);
1066 }
1067
1068 eRNAOverrideStatus RNA_property_override_library_status(PointerRNA *ptr,
1069                                                         PropertyRNA *prop,
1070                                                         const int index)
1071 {
1072   int override_status = 0;
1073
1074   if (!BKE_override_library_is_enabled()) {
1075     return override_status;
1076   }
1077
1078   if (!ptr || !prop || !ptr->id.data || !((ID *)ptr->id.data)->override_library) {
1079     return override_status;
1080   }
1081
1082   if (RNA_property_overridable_get(ptr, prop) && RNA_property_editable_flag(ptr, prop)) {
1083     override_status |= RNA_OVERRIDE_STATUS_OVERRIDABLE;
1084   }
1085
1086   IDOverrideLibraryPropertyOperation *opop = RNA_property_override_property_operation_find(
1087       ptr, prop, index, false, NULL);
1088   if (opop != NULL) {
1089     override_status |= RNA_OVERRIDE_STATUS_OVERRIDDEN;
1090     if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_MANDATORY) {
1091       override_status |= RNA_OVERRIDE_STATUS_MANDATORY;
1092     }
1093     if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_LOCKED) {
1094       override_status |= RNA_OVERRIDE_STATUS_LOCKED;
1095     }
1096   }
1097
1098   return override_status;
1099 }