Cleanup: Fix warning in makesdna
[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') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ||
88           (c == '_'));
89 }
90
91 uint DNA_elem_id_offset_start(const char *elem_full)
92 {
93   uint elem_full_offset = 0;
94   while (!is_identifier(elem_full[elem_full_offset])) {
95     elem_full_offset++;
96   }
97   return elem_full_offset;
98 }
99
100 uint DNA_elem_id_offset_end(const char *elem_full)
101 {
102   uint elem_full_offset = 0;
103   while (is_identifier(elem_full[elem_full_offset])) {
104     elem_full_offset++;
105   }
106   return elem_full_offset;
107 }
108
109 /**
110  * \a elem_dst must be at least the size of \a elem_src.
111  */
112 uint DNA_elem_id_strip_copy(char *elem_dst, const char *elem_src)
113 {
114   const uint elem_src_offset = DNA_elem_id_offset_start(elem_src);
115   const char *elem_src_trim = elem_src + elem_src_offset;
116   const uint elem_src_trim_len = DNA_elem_id_offset_end(elem_src_trim);
117   memcpy(elem_dst, elem_src_trim, elem_src_trim_len);
118   elem_dst[elem_src_trim_len] = '\0';
119   return elem_src_trim_len;
120 }
121
122 uint DNA_elem_id_strip(char *elem)
123 {
124   const uint elem_offset = DNA_elem_id_offset_start(elem);
125   const char *elem_trim = elem + elem_offset;
126   const uint elem_trim_len = DNA_elem_id_offset_end(elem_trim);
127   memmove(elem, elem_trim, elem_trim_len);
128   elem[elem_trim_len] = '\0';
129   return elem_trim_len;
130 }
131
132 /**
133  * Check if 'var' matches '*var[3]' for eg,
134  * return true if it does, with start/end offsets.
135  */
136 bool DNA_elem_id_match(const char *elem_search,
137                        const int elem_search_len,
138                        const char *elem_full,
139                        uint *r_elem_full_offset)
140 {
141   BLI_assert(strlen(elem_search) == elem_search_len);
142   const uint elem_full_offset = DNA_elem_id_offset_start(elem_full);
143   const char *elem_full_trim = elem_full + elem_full_offset;
144   if (strncmp(elem_search, elem_full_trim, elem_search_len) == 0) {
145     const char c = elem_full_trim[elem_search_len];
146     if (c == '\0' || !is_identifier(c)) {
147       *r_elem_full_offset = elem_full_offset;
148       return true;
149     }
150   }
151   return false;
152 }
153
154 /**
155  * Return a renamed dna name, allocated from \a mem_arena.
156  */
157 char *DNA_elem_id_rename(struct MemArena *mem_arena,
158                          const char *elem_src,
159                          const int elem_src_len,
160                          const char *elem_dst,
161                          const int elem_dst_len,
162                          const char *elem_src_full,
163                          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]) ^ BLI_ghashutil_strhash_p(pair[1]));
201 }
202
203 static bool strhash_pair_cmp(const void *a, const void *b)
204 {
205   const char *const *pair_a = a;
206   const char *const *pair_b = b;
207   return (STREQ(pair_a[0], pair_b[0]) && STREQ(pair_a[1], pair_b[1])) ? false : true;
208 }
209
210 void DNA_alias_maps(enum eDNA_RenameDir version_dir, GHash **r_struct_map, GHash **r_elem_map)
211 {
212   GHash *struct_map_local = NULL;
213   if (r_struct_map) {
214     const char *data[][2] = {
215 #define DNA_STRUCT_RENAME(old, new) {#old, #new},
216 #define DNA_STRUCT_RENAME_ELEM(struct_name, old, new)
217 #include "dna_rename_defs.h"
218 #undef DNA_STRUCT_RENAME
219 #undef DNA_STRUCT_RENAME_ELEM
220     };
221
222     int elem_key, elem_val;
223     if (version_dir == DNA_RENAME_ALIAS_FROM_STATIC) {
224       elem_key = 0;
225       elem_val = 1;
226     }
227     else {
228       elem_key = 1;
229       elem_val = 0;
230     }
231     GHash *struct_map = BLI_ghash_str_new_ex(__func__, ARRAY_SIZE(data));
232     for (int i = 0; i < ARRAY_SIZE(data); i++) {
233       BLI_ghash_insert(struct_map, (void *)data[i][elem_key], (void *)data[i][elem_val]);
234     }
235     *r_struct_map = struct_map;
236
237     /* We know the direction of this, for local use. */
238     struct_map_local = BLI_ghash_str_new_ex(__func__, ARRAY_SIZE(data));
239     for (int i = 0; i < ARRAY_SIZE(data); i++) {
240       BLI_ghash_insert(struct_map_local, (void *)data[i][1], (void *)data[i][0]);
241     }
242   }
243
244   if (r_elem_map != NULL) {
245     const char *data[][3] = {
246 #define DNA_STRUCT_RENAME(old, new)
247 #define DNA_STRUCT_RENAME_ELEM(struct_name, old, new) {#struct_name, #old, #new},
248 #include "dna_rename_defs.h"
249 #undef DNA_STRUCT_RENAME
250 #undef DNA_STRUCT_RENAME_ELEM
251     };
252
253     int elem_key, elem_val;
254     if (version_dir == DNA_RENAME_ALIAS_FROM_STATIC) {
255       elem_key = 1;
256       elem_val = 2;
257     }
258     else {
259       elem_key = 2;
260       elem_val = 1;
261     }
262     GHash *elem_map = BLI_ghash_new_ex(
263         strhash_pair_p, strhash_pair_cmp, __func__, ARRAY_SIZE(data));
264     for (int i = 0; i < ARRAY_SIZE(data); i++) {
265       const char **str_pair = MEM_mallocN(sizeof(char *) * 2, __func__);
266       str_pair[0] = BLI_ghash_lookup_default(struct_map_local, data[i][0], (void *)data[i][0]);
267       str_pair[1] = data[i][elem_key];
268       BLI_ghash_insert(elem_map, (void *)str_pair, (void *)data[i][elem_val]);
269     }
270     *r_elem_map = elem_map;
271   }
272
273   if (struct_map_local) {
274     BLI_ghash_free(struct_map_local, NULL, NULL);
275   }
276 }
277
278 #undef DNA_MAKESDNA
279
280 /** \} */
281
282 /* -------------------------------------------------------------------- */
283 /** \name Struct Name Legacy Hack
284  * \{ */
285
286 /**
287  * DNA Compatibility Hack
288  * ======================
289  *
290  * Only keep this for compatibility: **NEVER ADD NEW STRINGS HERE**.
291  *
292  * The renaming here isn't complete, references to the old struct names
293  * are still included in DNA, now fixing these struct names properly
294  * breaks forward compatibility. Leave these as-is, but don't add to them!
295  * See D4342#98780
296  */
297 const char *DNA_struct_rename_legacy_hack_static_from_alias(const char *name)
298 {
299   /* 'bScreen' replaces the old IrisGL 'Screen' struct */
300   if (STREQ("bScreen", name)) {
301     return "Screen";
302   }
303   /* Groups renamed to collections in 2.8 */
304   if (STREQ("Collection", name)) {
305     return "Group";
306   }
307   if (STREQ("CollectionObject", name)) {
308     return "GroupObject";
309   }
310   return name;
311 }
312
313 const char *DNA_struct_rename_legacy_hack_alias_from_static(const char *name)
314 {
315   /* 'bScreen' replaces the old IrisGL 'Screen' struct */
316   if (STREQ("Screen", name)) {
317     return "bScreen";
318   }
319   /* Groups renamed to collections in 2.8 */
320   if (STREQ("Group", name)) {
321     return "Collection";
322   }
323   if (STREQ("GroupObject", name)) {
324     return "CollectionObject";
325   }
326   return name;
327 }
328
329 /** \} */