add endian switch functions to replace macros SWITCH_INT/LONG/SHORT, with BLI_endian_...
[blender.git] / source / blender / blenkernel / intern / customdata_file.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  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/blenkernel/intern/customdata_file.c
22  *  \ingroup bke
23  */
24
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_fileops.h"
33 #include "BLI_string.h"
34 #include "BLI_utildefines.h"
35 #include "BLI_endian_switch.h"
36
37 #include "BKE_customdata_file.h"
38 #include "BKE_global.h"
39
40
41 /************************* File Format Definitions ***************************/
42
43 #define CDF_ENDIAN_LITTLE   0
44 #define CDF_ENDIAN_BIG      1
45
46 #define CDF_DATA_FLOAT  0
47
48 typedef struct CDataFileHeader {
49         char ID[4];                 /* "BCDF" */
50         char endian;                /* little, big */
51         char version;               /* non-compatible versions */
52         char subversion;            /* compatible sub versions */
53         char pad;                   /* padding */
54
55         int structbytes;            /* size of this struct in bytes */
56         int type;                   /* image, mesh */
57         int totlayer;               /* number of layers in the file */
58 } CDataFileHeader;
59
60 typedef struct CDataFileImageHeader {
61         int structbytes;            /* size of this struct in bytes */
62         int width;                  /* image width */
63         int height;                 /* image height */
64         int tile_size;              /* tile size (required power of 2) */
65 } CDataFileImageHeader;
66
67 typedef struct CDataFileMeshHeader {
68         int structbytes;            /* size of this struct in bytes */
69 } CDataFileMeshHeader;
70
71 struct CDataFileLayer {
72         int structbytes;                /* size of this struct in bytes */
73         int datatype;                   /* only float for now */
74         uint64_t datasize;              /* size of data in layer */
75         int type;                       /* layer type */
76         char name[CDF_LAYER_NAME_MAX];  /* layer name */
77 };
78
79 /**************************** Other Definitions ******************************/
80
81 #define CDF_VERSION         0
82 #define CDF_SUBVERSION      0
83 #define CDF_TILE_SIZE       64
84
85 struct CDataFile {
86         int type;
87
88         CDataFileHeader header;
89         union {
90                 CDataFileImageHeader image;
91                 CDataFileMeshHeader mesh;
92         } btype;
93
94         CDataFileLayer *layer;
95         int totlayer;
96
97         FILE *readf;
98         FILE *writef;
99         int switchendian;
100         size_t dataoffset;
101 };
102
103 /********************************* Create/Free *******************************/
104
105 static int cdf_endian(void)
106 {
107         if (ENDIAN_ORDER == L_ENDIAN)
108                 return CDF_ENDIAN_LITTLE;
109         else
110                 return CDF_ENDIAN_BIG;
111 }
112
113 #if 0
114 static int cdf_data_type_size(int datatype)
115 {
116         if (datatype == CDF_DATA_FLOAT)
117                 return sizeof(float);
118         
119         return 0;
120 }
121 #endif
122
123 CDataFile *cdf_create(int type)
124 {
125         CDataFile *cdf = MEM_callocN(sizeof(CDataFile), "CDataFile");
126
127         cdf->type = type;
128
129         return cdf;
130 }
131
132 void cdf_free(CDataFile *cdf)
133 {
134         cdf_read_close(cdf);
135         cdf_write_close(cdf);
136
137         if (cdf->layer)
138                 MEM_freeN(cdf->layer);
139
140         MEM_freeN(cdf);
141 }
142
143 /********************************* Read/Write ********************************/
144
145 static int cdf_read_header(CDataFile *cdf)
146 {
147         CDataFileHeader *header;
148         CDataFileImageHeader *image;
149         CDataFileMeshHeader *mesh;
150         CDataFileLayer *layer;
151         FILE *f = cdf->readf;
152         size_t offset = 0;
153         int a;
154
155         header = &cdf->header;
156
157         if (!fread(header, sizeof(CDataFileHeader), 1, cdf->readf))
158                 return 0;
159         
160         if (memcmp(header->ID, "BCDF", sizeof(header->ID)) != 0)
161                 return 0;
162         if (header->version > CDF_VERSION)
163                 return 0;
164
165         cdf->switchendian = header->endian != cdf_endian();
166         header->endian = cdf_endian();
167
168         if (cdf->switchendian) {
169                 BLI_endian_switch_int32(&header->type);
170                 BLI_endian_switch_int32(&header->totlayer);
171                 BLI_endian_switch_int32(&header->structbytes);
172         }
173
174         if (!ELEM(header->type, CDF_TYPE_IMAGE, CDF_TYPE_MESH))
175                 return 0;
176
177         offset += header->structbytes;
178         header->structbytes = sizeof(CDataFileHeader);
179
180         if (fseek(f, offset, SEEK_SET) != 0)
181                 return 0;
182         
183         if (header->type == CDF_TYPE_IMAGE) {
184                 image = &cdf->btype.image;
185                 if (!fread(image, sizeof(CDataFileImageHeader), 1, f))
186                         return 0;
187
188                 if (cdf->switchendian) {
189                         BLI_endian_switch_int32(&image->width);
190                         BLI_endian_switch_int32(&image->height);
191                         BLI_endian_switch_int32(&image->tile_size);
192                         BLI_endian_switch_int32(&image->structbytes);
193                 }
194
195                 offset += image->structbytes;
196                 image->structbytes = sizeof(CDataFileImageHeader);
197         }
198         else if (header->type == CDF_TYPE_MESH) {
199                 mesh = &cdf->btype.mesh;
200                 if (!fread(mesh, sizeof(CDataFileMeshHeader), 1, f))
201                         return 0;
202
203                 if (cdf->switchendian)
204                         BLI_endian_switch_int32(&mesh->structbytes);
205
206                 offset += mesh->structbytes;
207                 mesh->structbytes = sizeof(CDataFileMeshHeader);
208         }
209
210         if (fseek(f, offset, SEEK_SET) != 0)
211                 return 0;
212
213         cdf->layer = MEM_callocN(sizeof(CDataFileLayer) * header->totlayer, "CDataFileLayer");
214         cdf->totlayer = header->totlayer;
215
216         for (a = 0; a < header->totlayer; a++) {
217                 layer = &cdf->layer[a];
218
219                 if (!fread(layer, sizeof(CDataFileLayer), 1, f))
220                         return 0;
221
222                 if (cdf->switchendian) {
223                         BLI_endian_switch_int32(&layer->type);
224                         BLI_endian_switch_int32(&layer->datatype);
225                         BLI_endian_switch_uint64(&layer->datasize);
226                         BLI_endian_switch_int32(&layer->structbytes);
227                 }
228
229                 if (layer->datatype != CDF_DATA_FLOAT)
230                         return 0;
231
232                 offset += layer->structbytes;
233                 layer->structbytes = sizeof(CDataFileLayer);
234
235                 if (fseek(f, offset, SEEK_SET) != 0)
236                         return 0;
237         }
238
239         cdf->dataoffset = offset;
240
241         return 1;
242 }
243
244 static int cdf_write_header(CDataFile *cdf)
245 {
246         CDataFileHeader *header;
247         CDataFileImageHeader *image;
248         CDataFileMeshHeader *mesh;
249         CDataFileLayer *layer;
250         FILE *f = cdf->writef;
251         int a;
252
253         header = &cdf->header;
254
255         if (!fwrite(header, sizeof(CDataFileHeader), 1, f))
256                 return 0;
257         
258         if (header->type == CDF_TYPE_IMAGE) {
259                 image = &cdf->btype.image;
260                 if (!fwrite(image, sizeof(CDataFileImageHeader), 1, f))
261                         return 0;
262         }
263         else if (header->type == CDF_TYPE_MESH) {
264                 mesh = &cdf->btype.mesh;
265                 if (!fwrite(mesh, sizeof(CDataFileMeshHeader), 1, f))
266                         return 0;
267         }
268
269         for (a = 0; a < header->totlayer; a++) {
270                 layer = &cdf->layer[a];
271
272                 if (!fwrite(layer, sizeof(CDataFileLayer), 1, f))
273                         return 0;
274         }
275
276         return 1;
277 }
278
279 int cdf_read_open(CDataFile *cdf, const char *filename)
280 {
281         FILE *f;
282
283         f = BLI_fopen(filename, "rb");
284         if (!f)
285                 return 0;
286         
287         cdf->readf = f;
288
289         if (!cdf_read_header(cdf)) {
290                 cdf_read_close(cdf);
291                 return 0;
292         }
293
294         if (cdf->header.type != cdf->type) {
295                 cdf_read_close(cdf);
296                 return 0;
297         }
298
299         return 1;
300 }
301
302 int cdf_read_layer(CDataFile *cdf, CDataFileLayer *blay)
303 {
304         size_t offset;
305         int a;
306
307         /* seek to right location in file */
308         offset = cdf->dataoffset;
309         for (a = 0; a < cdf->totlayer; a++) {
310                 if (&cdf->layer[a] == blay)
311                         break;
312                 else
313                         offset += cdf->layer[a].datasize;
314         }
315
316         return (fseek(cdf->readf, offset, SEEK_SET) == 0);
317 }
318
319 int cdf_read_data(CDataFile *cdf, unsigned int size, void *data)
320 {
321         float *fdata;
322         unsigned int a;
323
324         /* read data */
325         if (!fread(data, size, 1, cdf->readf))
326                 return 0;
327
328         /* switch endian if necessary */
329         if (cdf->switchendian) {
330                 fdata = data;
331
332                 for (a = 0; a < size / sizeof(float); a++) {
333                         BLI_endian_switch_float(&fdata[a]);
334                 }
335         }
336
337         return 1;
338 }
339
340 void cdf_read_close(CDataFile *cdf)
341 {
342         if (cdf->readf) {
343                 fclose(cdf->readf);
344                 cdf->readf = NULL;
345         }
346 }
347
348 int cdf_write_open(CDataFile *cdf, const char *filename)
349 {
350         CDataFileHeader *header;
351         CDataFileImageHeader *image;
352         CDataFileMeshHeader *mesh;
353         FILE *f;
354
355         f = BLI_fopen(filename, "wb");
356         if (!f)
357                 return 0;
358         
359         cdf->writef = f;
360
361         /* fill header */
362         header = &cdf->header;
363         /* strcpy(, "BCDF"); // terminator out of range */
364         header->ID[0] = 'B'; header->ID[1] = 'C'; header->ID[2] = 'D'; header->ID[3] = 'F';
365         header->endian = cdf_endian();
366         header->version = CDF_VERSION;
367         header->subversion = CDF_SUBVERSION;
368
369         header->structbytes = sizeof(CDataFileHeader);
370         header->type = cdf->type;
371         header->totlayer = cdf->totlayer;
372
373         if (cdf->type == CDF_TYPE_IMAGE) {
374                 /* fill image header */
375                 image = &cdf->btype.image;
376                 image->structbytes = sizeof(CDataFileImageHeader);
377                 image->tile_size = CDF_TILE_SIZE;
378         }
379         else if (cdf->type == CDF_TYPE_MESH) {
380                 /* fill mesh header */
381                 mesh = &cdf->btype.mesh;
382                 mesh->structbytes = sizeof(CDataFileMeshHeader);
383         }
384
385         cdf_write_header(cdf);
386
387         return 1;
388 }
389
390 int cdf_write_layer(CDataFile *UNUSED(cdf), CDataFileLayer *UNUSED(blay))
391 {
392         return 1;
393 }
394
395 int cdf_write_data(CDataFile *cdf, unsigned int size, void *data)
396 {
397         /* write data */
398         if (!fwrite(data, size, 1, cdf->writef))
399                 return 0;
400
401         return 1;
402 }
403
404 void cdf_write_close(CDataFile *cdf)
405 {
406         if (cdf->writef) {
407                 fclose(cdf->writef);
408                 cdf->writef = NULL;
409         }
410 }
411
412 void cdf_remove(const char *filename)
413 {
414         BLI_delete(filename, 0, 0);
415 }
416
417 /********************************** Layers ***********************************/
418
419 CDataFileLayer *cdf_layer_find(CDataFile *cdf, int type, const char *name)
420 {
421         CDataFileLayer *layer;
422         int a;
423
424         for (a = 0; a < cdf->totlayer; a++) {
425                 layer = &cdf->layer[a];
426
427                 if (layer->type == type && strcmp(layer->name, name) == 0)
428                         return layer;
429         }
430         
431         return NULL;
432 }
433
434 CDataFileLayer *cdf_layer_add(CDataFile *cdf, int type, const char *name, size_t datasize)
435 {
436         CDataFileLayer *newlayer, *layer;
437
438         /* expand array */
439         newlayer = MEM_callocN(sizeof(CDataFileLayer) * (cdf->totlayer + 1), "CDataFileLayer");
440         memcpy(newlayer, cdf->layer, sizeof(CDataFileLayer) * cdf->totlayer);
441         cdf->layer = newlayer;
442
443         cdf->totlayer++;
444
445         /* fill in new layer */
446         layer = &cdf->layer[cdf->totlayer - 1];
447         layer->structbytes = sizeof(CDataFileLayer);
448         layer->datatype = CDF_DATA_FLOAT;
449         layer->datasize = datasize;
450         layer->type = type;
451         BLI_strncpy(layer->name, name, CDF_LAYER_NAME_MAX);
452
453         return layer;
454 }
455