Merge branch 'blender2.7'
[blender.git] / source / blender / makesdna / intern / dna_utils.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  * Copyright (C) 2018 Blender Foundation.
17  */
18
19 /** \file
20  * \ingroup DNA
21  *
22  * Utilities for stand-alone makesdna.c and Blender to share.
23  */
24
25 #include <string.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_sys_types.h"
30 #include "BLI_utildefines.h"
31 #include "BLI_assert.h"
32 #include "BLI_ghash.h"
33
34 #include "BLI_memarena.h"
35
36 #include "dna_utils.h"
37
38 /* -------------------------------------------------------------------- */
39 /** \name Struct Member Evaluation
40  * \{ */
41
42 /**
43  * Parses the `[n1][n2]...` on the end of an array name
44  * and returns the number of array elements `n1 * n2 ...`.
45  */
46 int DNA_elem_array_size(const char *str)
47 {
48         int result = 1;
49         int current = 0;
50         while (true) {
51                 char c = *str++;
52                 switch (c) {
53                         case '\0':
54                                 return result;
55                         case '[':
56                                 current = 0;
57                                 break;
58                         case ']':
59                                 result *= current;
60                                 break;
61                         case '0':
62                         case '1':
63                         case '2':
64                         case '3':
65                         case '4':
66                         case '5':
67                         case '6':
68                         case '7':
69                         case '8':
70                         case '9':
71                                 current = current * 10 + (c - '0');
72                                 break;
73                         default:
74                                 break;
75                 }
76         }
77 }
78
79 /** \} */
80
81 /* -------------------------------------------------------------------- */
82 /** \name Struct Member Manipulation
83  * \{ */
84
85 static bool is_identifier(const char c)
86 {
87         return ((c >= 'a' && c <= 'z') ||
88                 (c >= 'A' && c <= 'Z') ||
89                 (c >= '0' && c <= '9') ||
90                 (c == '_'));
91 }
92
93 uint DNA_elem_id_offset_start(const char *elem_full)
94 {
95         uint elem_full_offset = 0;
96         while (!is_identifier(elem_full[elem_full_offset])) {
97                 elem_full_offset++;
98         }
99         return elem_full_offset;
100 }
101
102 uint DNA_elem_id_offset_end(const char *elem_full)
103 {
104         uint elem_full_offset = 0;
105         while (is_identifier(elem_full[elem_full_offset])) {
106                 elem_full_offset++;
107         }
108         return elem_full_offset;
109 }
110
111 /**
112  * \a elem_dst must be at least the size of \a elem_src.
113  */
114 uint DNA_elem_id_strip_copy(char *elem_dst, const char *elem_src)
115 {
116         const uint elem_src_offset = DNA_elem_id_offset_start(elem_src);
117         const char *elem_src_trim = elem_src + elem_src_offset;
118         const uint elem_src_trim_len = DNA_elem_id_offset_end(elem_src_trim);
119         memcpy(elem_dst, elem_src_trim, elem_src_trim_len);
120         elem_dst[elem_src_trim_len] = '\0';
121         return elem_src_trim_len;
122 }
123
124 uint DNA_elem_id_strip(char *elem)
125 {
126         const uint elem_offset = DNA_elem_id_offset_start(elem);
127         const char *elem_trim = elem + elem_offset;
128         const uint elem_trim_len = DNA_elem_id_offset_end(elem_trim);
129         memmove(elem, elem_trim, elem_trim_len);
130         elem[elem_trim_len] = '\0';
131         return elem_trim_len;
132 }
133
134 /**
135  * Check if 'var' matches '*var[3]' for eg,
136  * return true if it does, with start/end offsets.
137  */
138 bool DNA_elem_id_match(
139         const char *elem_search, const int elem_search_len,
140         const char *elem_full,
141         uint *r_elem_full_offset)
142 {
143         BLI_assert(strlen(elem_search) == elem_search_len);
144         const uint elem_full_offset = DNA_elem_id_offset_start(elem_full);
145         const char *elem_full_trim = elem_full + elem_full_offset;
146         if (strncmp(elem_search, elem_full_trim, elem_search_len) == 0) {
147                 const char c = elem_full_trim[elem_search_len];
148                 if (c == '\0' || !is_identifier(c)) {
149                         *r_elem_full_offset = elem_full_offset;
150                         return true;
151                 }
152         }
153         return false;
154 }
155
156 /**
157  * Return a renamed dna name, allocated from \a mem_arena.
158  */
159 char *DNA_elem_id_rename(
160         struct MemArena *mem_arena,
161         const char *elem_src, const int elem_src_len,
162         const char *elem_dst, const int elem_dst_len,
163         const char *elem_src_full, const int elem_src_full_len,
164         const uint elem_src_full_offset_len)
165 {
166         BLI_assert(strlen(elem_src) == elem_src_len);
167         BLI_assert(strlen(elem_dst) == elem_dst_len);
168         BLI_assert(strlen(elem_src_full) == elem_src_full_len);
169         BLI_assert(DNA_elem_id_offset_start(elem_src_full) == elem_src_full_offset_len);
170         UNUSED_VARS_NDEBUG(elem_src);
171
172         const int elem_final_len = (elem_src_full_len - elem_src_len) + elem_dst_len;
173         char *elem_dst_full = BLI_memarena_alloc(mem_arena, elem_final_len + 1);
174         uint i = 0;
175         if (elem_src_full_offset_len != 0) {
176                 memcpy(elem_dst_full, elem_src_full, elem_src_full_offset_len);
177                 i = elem_src_full_offset_len;
178         }
179         memcpy(&elem_dst_full[i], elem_dst, elem_dst_len + 1);
180         i += elem_dst_len;
181         uint elem_src_full_offset_end = elem_src_full_offset_len + elem_src_len;
182         if (elem_src_full[elem_src_full_offset_end] != '\0') {
183                 const int elem_full_tail_len = (elem_src_full_len - elem_src_full_offset_end);
184                 memcpy(&elem_dst_full[i], &elem_src_full[elem_src_full_offset_end], elem_full_tail_len + 1);
185                 i += elem_full_tail_len;
186         }
187         BLI_assert((strlen(elem_dst_full) == elem_final_len) && (i == elem_final_len));
188         return elem_dst_full;
189 }
190
191 /** \} */
192
193 /* -------------------------------------------------------------------- */
194 /** \name Versioning
195  * \{ */
196
197 static uint strhash_pair_p(const void *ptr)
198 {
199         const char * const *pair = ptr;
200         return (BLI_ghashutil_strhash_p(pair[0]) ^
201                 BLI_ghashutil_strhash_p(pair[1]));
202 }
203
204 static bool strhash_pair_cmp(const void *a, const void *b)
205 {
206         const char * const *pair_a = a;
207         const char * const *pair_b = b;
208         return (STREQ(pair_a[0], pair_b[0]) &&
209                 STREQ(pair_a[1], pair_b[1])) ? false : true;
210 }
211
212 void DNA_alias_maps(
213         enum eDNA_RenameDir version_dir,
214         GHash **r_struct_map, GHash **r_elem_map)
215 {
216         GHash *struct_map_local = NULL;
217         if (r_struct_map) {
218                 const char *data[][2] = {
219 #define DNA_STRUCT_RENAME(old, new) {#old, #new},
220 #define DNA_STRUCT_RENAME_ELEM(struct_name, old, new)
221 #include "dna_rename_defs.h"
222 #undef DNA_STRUCT_RENAME
223 #undef DNA_STRUCT_RENAME_ELEM
224                 };
225
226                 int elem_key, elem_val;
227                 if (version_dir == DNA_RENAME_ALIAS_FROM_STATIC) {
228                         elem_key = 0;
229                         elem_val = 1;
230                 }
231                 else {
232                         elem_key = 1;
233                         elem_val = 0;
234                 }
235                 GHash *struct_map = BLI_ghash_str_new_ex(__func__, ARRAY_SIZE(data));
236                 for (int i = 0; i < ARRAY_SIZE(data); i++) {
237                         BLI_ghash_insert(struct_map, (void *)data[i][elem_key], (void *)data[i][elem_val]);
238                 }
239                 *r_struct_map = struct_map;
240
241                 /* We know the direction of this, for local use. */
242                 struct_map_local = BLI_ghash_str_new_ex(__func__, ARRAY_SIZE(data));
243                 for (int i = 0; i < ARRAY_SIZE(data); i++) {
244                         BLI_ghash_insert(struct_map_local, (void *)data[i][1], (void *)data[i][0]);
245                 }
246         }
247
248         if (r_elem_map != NULL) {
249                 const char *data[][3] = {
250 #define DNA_STRUCT_RENAME(old, new)
251 #define DNA_STRUCT_RENAME_ELEM(struct_name, old, new) {#struct_name, #old, #new},
252 #include "dna_rename_defs.h"
253 #undef DNA_STRUCT_RENAME
254 #undef DNA_STRUCT_RENAME_ELEM
255                 };
256
257                 int elem_key, elem_val;
258                 if (version_dir == DNA_RENAME_ALIAS_FROM_STATIC) {
259                         elem_key = 1;
260                         elem_val = 2;
261                 }
262                 else {
263                         elem_key = 2;
264                         elem_val = 1;
265                 }
266                 GHash *elem_map = BLI_ghash_new_ex(strhash_pair_p, strhash_pair_cmp, __func__, ARRAY_SIZE(data));
267                 for (int i = 0; i < ARRAY_SIZE(data); i++) {
268                         const char **str_pair = MEM_mallocN(sizeof(char *) * 2, __func__);
269                         str_pair[0] = BLI_ghash_lookup_default(struct_map_local, data[i][0], (void *)data[i][0]);
270                         str_pair[1] = data[i][elem_key],
271                         BLI_ghash_insert(elem_map, str_pair, (void *)data[i][elem_val]);
272                 }
273                 *r_elem_map = elem_map;
274         }
275
276         if (struct_map_local) {
277                 BLI_ghash_free(struct_map_local, NULL, NULL);
278         }
279 }
280
281 #undef DNA_MAKESDNA
282
283 /** \} */
284
285 /* -------------------------------------------------------------------- */
286 /** \name Struct Name Legacy Hack
287  * \{ */
288
289 /**
290  * DNA Compatibility Hack
291  * ======================
292  *
293  * Only keep this for compatibility: **NEVER ADD NEW STRINGS HERE**.
294  *
295  * The renaming here isn't complete, references to the old struct names
296  * are still included in DNA, now fixing these struct names properly
297  * breaks forward compatibility. Leave these as-is, but don't add to them!
298  * See D4342#98780
299  */
300 const char *DNA_struct_rename_legacy_hack_static_from_alias(const char *name)
301 {
302         /* 'bScreen' replaces the old IrisGL 'Screen' struct */
303         if (STREQ("bScreen", name)) {
304                 return "Screen";
305         }
306         /* Groups renamed to collections in 2.8 */
307         if (STREQ("Collection", name)) {
308                 return "Group";
309         }
310         if (STREQ("CollectionObject", name)) {
311                 return "GroupObject";
312         }
313         return name;
314 }
315
316 const char *DNA_struct_rename_legacy_hack_alias_from_static(const char *name)
317 {
318         /* 'bScreen' replaces the old IrisGL 'Screen' struct */
319         if (STREQ("Screen", name)) {
320                 return "bScreen";
321         }
322         /* Groups renamed to collections in 2.8 */
323         if (STREQ("Group", name)) {
324                 return "Collection";
325         }
326         if (STREQ("GroupObject", name)) {
327                 return "CollectionObject";
328         }
329         return name;
330 }
331
332 /** \} */