Fix integer overflows in meshcache modifier.
[blender.git] / source / blender / modifiers / intern / MOD_meshcache_mdd.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Campbell Barton, pkowal
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/modifiers/intern/MOD_meshcache_mdd.c
24  *  \ingroup modifiers
25  */
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30
31 #include "BLI_sys_types.h"
32 #include "BLI_utildefines.h"
33 #include "BLI_fileops.h"
34 #include "BLI_math.h"
35 #ifdef __LITTLE_ENDIAN__
36 #  include "BLI_endian_switch.h"
37 #endif
38 #ifdef WIN32
39 #  include "BLI_winstuff.h"
40 #endif
41
42 #include "MOD_meshcache_util.h"  /* own include */
43
44 #include "DNA_modifier_types.h"
45
46 typedef struct MDDHead {
47         int frame_tot;
48         int verts_tot;
49 } MDDHead;  /* frames, verts */
50
51 static bool meshcache_read_mdd_head(FILE *fp, const int verts_tot,
52                                     MDDHead *mdd_head,
53                                     const char **err_str)
54 {
55         if (!fread(mdd_head, sizeof(*mdd_head), 1, fp)) {
56                 *err_str = "Missing header";
57                 return false;
58         }
59
60 #ifdef __LITTLE_ENDIAN__
61         BLI_endian_switch_int32_array((int *)mdd_head, 2);
62 #endif
63
64         if (mdd_head->verts_tot != verts_tot) {
65                 *err_str = "Vertex count mismatch";
66                 return false;
67         }
68
69         if (mdd_head->frame_tot <= 0) {
70                 *err_str = "Invalid frame total";
71                 return false;
72         }
73         /* intentionally dont seek back */
74
75         return true;
76 }
77
78 /**
79  * Gets the index frange and factor
80  */
81 static bool meshcache_read_mdd_range(FILE *fp,
82                                      const int verts_tot,
83                                      const float frame, const char interp,
84                                      int r_index_range[2], float *r_factor,
85                                      const char **err_str)
86 {
87         MDDHead mdd_head;
88
89         /* first check interpolation and get the vert locations */
90
91         if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
92                 return false;
93         }
94
95         MOD_meshcache_calc_range(frame, interp, mdd_head.frame_tot, r_index_range, r_factor);
96
97         return true;
98 }
99
100 static bool meshcache_read_mdd_range_from_time(FILE *fp,
101                                                const int verts_tot,
102                                                const float time, const float UNUSED(fps),
103                                                float *r_frame,
104                                                const char **err_str)
105 {
106         MDDHead mdd_head;
107         int i;
108         float f_time, f_time_prev = FLT_MAX;
109         float frame;
110
111         if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
112                 return false;
113         }
114
115         for (i = 0; i < mdd_head.frame_tot; i++) {
116                 fread(&f_time, sizeof(float), 1, fp);
117 #ifdef __LITTLE_ENDIAN__
118                 BLI_endian_switch_float(&f_time);
119 #endif
120                 if (f_time >= time) {
121                         break;
122                 }
123                 f_time_prev = f_time;
124         }
125
126         if (i == mdd_head.frame_tot) {
127                 frame = (float)(mdd_head.frame_tot - 1);
128         }
129         if (UNLIKELY(f_time_prev == FLT_MAX)) {
130                 frame = 0.0f;
131         }
132         else {
133                 const float range  = f_time - f_time_prev;
134
135                 if (range <= FRAME_SNAP_EPS) {
136                         frame = (float)i;
137                 }
138                 else {
139                         frame = (float)(i - 1) + ((time - f_time_prev) / range);
140                 }
141         }
142
143         *r_frame = frame;
144         return true;
145 }
146
147 bool MOD_meshcache_read_mdd_index(FILE *fp,
148                                   float (*vertexCos)[3], const int verts_tot,
149                                   const int index, const float factor,
150                                   const char **err_str)
151 {
152         MDDHead mdd_head;
153
154         if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
155                 return false;
156         }
157
158         if (fseek(fp, mdd_head.frame_tot * sizeof(int), SEEK_CUR) != 0) {
159                 *err_str = "Header seek failed";
160                 return false;
161         }
162
163         if (fseek(fp, sizeof(float) * 3 * index * mdd_head.verts_tot, SEEK_CUR) != 0) {
164                 *err_str = "Failed to seek frame";
165                 return false;
166         }
167
168         if (factor >= 1.0f) {
169 #if 1
170                 float *vco = *vertexCos;
171                 unsigned int i;
172                 for (i = mdd_head.verts_tot; i != 0 ; i--, vco += 3) {
173                         fread(vco, sizeof(float) * 3, 1, fp);
174
175 #  ifdef __LITTLE_ENDIAN__
176                         BLI_endian_switch_float(vco + 0);
177                         BLI_endian_switch_float(vco + 1);
178                         BLI_endian_switch_float(vco + 2);
179 #  endif  /* __LITTLE_ENDIAN__ */
180                 }
181 #else
182                 /* no blending */
183                 if (!fread(vertexCos, sizeof(float) * 3, mdd_head.verts_tot, f)) {
184                         *err_str = errno ? strerror(errno) : "Failed to read frame";
185                         return false;
186                 }
187 #  ifdef __LITTLE_ENDIAN__
188                 BLI_endian_switch_float_array(vertexCos[0], mdd_head.verts_tot * 3);
189 #  endif
190 #endif
191         }
192         else {
193                 const float ifactor = 1.0f - factor;
194                 float *vco = *vertexCos;
195                 unsigned int i;
196                 for (i = mdd_head.verts_tot; i != 0 ; i--, vco += 3) {
197                         float tvec[3];
198                         fread(tvec, sizeof(float) * 3, 1, fp);
199
200 #ifdef __LITTLE_ENDIAN__
201                         BLI_endian_switch_float(tvec + 0);
202                         BLI_endian_switch_float(tvec + 1);
203                         BLI_endian_switch_float(tvec + 2);
204 #endif
205
206                         vco[0] = (vco[0] * ifactor) + (tvec[0] * factor);
207                         vco[1] = (vco[1] * ifactor) + (tvec[1] * factor);
208                         vco[2] = (vco[2] * ifactor) + (tvec[2] * factor);
209                 }
210         }
211
212         return true;
213 }
214
215 bool MOD_meshcache_read_mdd_frame(FILE *fp,
216                                   float (*vertexCos)[3], const int verts_tot, const char interp,
217                                   const float frame,
218                                   const char **err_str)
219 {
220         int index_range[2];
221         float factor;
222
223         if (meshcache_read_mdd_range(fp, verts_tot, frame, interp,
224                                      index_range, &factor,  /* read into these values */
225                                      err_str) == false)
226         {
227                 return false;
228         }
229
230         if (index_range[0] == index_range[1]) {
231                 /* read single */
232                 if ((fseek(fp, 0, SEEK_SET) == 0) &&
233                     MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str))
234                 {
235                         return true;
236                 }
237                 else {
238                         return false;
239                 }
240         }
241         else {
242                 /* read both and interpolate */
243                 if ((fseek(fp, 0, SEEK_SET) == 0) &&
244                     MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[0], 1.0f, err_str) &&
245                     (fseek(fp, 0, SEEK_SET) == 0) &&
246                     MOD_meshcache_read_mdd_index(fp, vertexCos, verts_tot, index_range[1], factor, err_str))
247                 {
248                         return true;
249                 }
250                 else {
251                         return false;
252                 }
253         }
254 }
255
256 bool MOD_meshcache_read_mdd_times(const char *filepath,
257                                   float (*vertexCos)[3], const int verts_tot, const char interp,
258                                   const float time, const float fps, const char time_mode,
259                                   const char **err_str)
260 {
261         float frame;
262
263         FILE *fp = BLI_fopen(filepath, "rb");
264         bool ok;
265
266         if (fp == NULL) {
267                 *err_str = errno ? strerror(errno) : "Unknown error opening file";
268                 return false;
269         }
270
271         switch (time_mode) {
272                 case MOD_MESHCACHE_TIME_FRAME:
273                 {
274                         frame = time;
275                         break;
276                 }
277                 case MOD_MESHCACHE_TIME_SECONDS:
278                 {
279                         /* we need to find the closest time */
280                         if (meshcache_read_mdd_range_from_time(fp, verts_tot, time, fps, &frame, err_str) == false) {
281                                 fclose(fp);
282                                 return false;
283                         }
284                         rewind(fp);
285                         break;
286                 }
287                 case MOD_MESHCACHE_TIME_FACTOR:
288                 default:
289                 {
290                         MDDHead mdd_head;
291                         if (meshcache_read_mdd_head(fp, verts_tot, &mdd_head, err_str) == false) {
292                                 fclose(fp);
293                                 return false;
294                         }
295
296                         frame = CLAMPIS(time, 0.0f, 1.0f) * (float)mdd_head.frame_tot;
297                         rewind(fp);
298                         break;
299                 }
300         }
301
302         ok = MOD_meshcache_read_mdd_frame(fp, vertexCos, verts_tot, interp, frame, err_str);
303
304         fclose(fp);
305         return ok;
306 }