1 /* ----------------------------------------------------------------------
2 * RPly library, read/write PLY files
3 * Diego Nehab, IMPA
4 * http://www.impa.br/~diego/software/rply
5 *
6 * This library is distributed under the MIT License. See notice
7 * at the end of this file.
8 * ---------------------------------------------------------------------- */
9 #include <stdio.h>
10 #include <ctype.h>
11 #include <assert.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <float.h>
15 #include <stdarg.h>
16 #include <stdlib.h>
17 #include <stddef.h>
18
19 #include "rply.h"
20
21 /* ----------------------------------------------------------------------
22 * Make sure we get our integer types right
23 * ---------------------------------------------------------------------- */
24 #if defined(_MSC_VER) && (_MSC_VER < 1600)
25 /* C99 stdint.h only supported in MSVC++ 10.0 and up */
26 typedef __int8 t_ply_int8;
27 typedef __int16 t_ply_int16;
28 typedef __int32 t_ply_int32;
29 typedef unsigned __int8 t_ply_uint8;
30 typedef unsigned __int16 t_ply_uint16;
31 typedef unsigned __int32 t_ply_uint32;
32 #define PLY_INT8_MAX (127)
33 #define PLY_INT8_MIN (-PLY_INT8_MAX-1)
34 #define PLY_INT16_MAX (32767)
35 #define PLY_INT16_MIN (-PLY_INT16_MAX-1)
36 #define PLY_INT32_MAX (2147483647)
37 #define PLY_INT32_MIN (-PLY_INT32_MAX-1)
38 #define PLY_UINT8_MAX (255)
39 #define PLY_UINT16_MAX (65535)
40 #define PLY_UINT32_MAX (4294967295)
41 #else
42 #include <stdint.h>
43 typedef int8_t t_ply_int8;
44 typedef int16_t t_ply_int16;
45 typedef int32_t t_ply_int32;
46 typedef uint8_t t_ply_uint8;
47 typedef uint16_t t_ply_uint16;
48 typedef uint32_t t_ply_uint32;
49 #define PLY_INT8_MIN INT8_MIN
50 #define PLY_INT8_MAX INT8_MAX
51 #define PLY_INT16_MIN INT16_MIN
52 #define PLY_INT16_MAX INT16_MAX
53 #define PLY_INT32_MIN INT32_MIN
54 #define PLY_INT32_MAX INT32_MAX
55 #define PLY_UINT8_MAX UINT8_MAX
56 #define PLY_UINT16_MAX UINT16_MAX
57 #define PLY_UINT32_MAX UINT32_MAX
58 #endif
59
60 /* ----------------------------------------------------------------------
61 * Constants
62 * ---------------------------------------------------------------------- */
63 #define WORDSIZE 256
64 #define LINESIZE 1024
65 #define BUFFERSIZE (8*1024)
66
67 typedef enum e_ply_io_mode_ {
68 PLY_READ,
69 PLY_WRITE
70 } e_ply_io_mode;
71
72 static const char *const ply_storage_mode_list[] = {
73 "binary_big_endian", "binary_little_endian", "ascii", NULL
74 }; /* order matches e_ply_storage_mode enum */
75
76 static const char *const ply_type_list[] = {
77 "int8", "uint8", "int16", "uint16",
78 "int32", "uint32", "float32", "float64",
79 "char", "uchar", "short", "ushort",
80 "int", "uint", "float", "double",
81 "list", NULL
82 }; /* order matches e_ply_type enum */
83
84 /* ----------------------------------------------------------------------
85 * Property reading callback argument
86 *
87 * element: name of element being processed
88 * property: name of property being processed
89 * nelements: number of elements of this kind in file
90 * instance_index: index current element of this kind being processed
91 * length: number of values in current list (or 1 for scalars)
92 * value_index: index of current value int this list (or 0 for scalars)
93 * value: value of property
94 * pdata/idata: user data defined with ply_set_cb
95 *
96 * Returns handle to PLY file if succesful, NULL otherwise.
97 * ---------------------------------------------------------------------- */
98 typedef struct t_ply_argument_ {
99 p_ply_element element;
100 long instance_index;
101 p_ply_property property;
102 long length, value_index;
103 double value;
104 void *pdata;
105 long idata;
106 } t_ply_argument;
107
108 /* ----------------------------------------------------------------------
109 * Property information
110 *
111 * name: name of this property
112 * type: type of this property (list or type of scalar value)
113 * length_type, value_type: type of list property count and values
114 * read_cb: function to be called when this property is called
115 *
116 * Returns 1 if should continue processing file, 0 if should abort.
117 * ---------------------------------------------------------------------- */
118 typedef struct t_ply_property_ {
119 char name[WORDSIZE];
120 e_ply_type type, value_type, length_type;
121 p_ply_read_cb read_cb;
122 void *pdata;
123 long idata;
124 } t_ply_property;
125
126 /* ----------------------------------------------------------------------
127 * Element information
128 *
129 * name: name of this property
130 * ninstances: number of elements of this type in file
131 * property: property descriptions for this element
132 * nproperty: number of properties in this element
133 *
134 * Returns 1 if should continue processing file, 0 if should abort.
135 * ---------------------------------------------------------------------- */
136 typedef struct t_ply_element_ {
137 char name[WORDSIZE];
138 long ninstances;
139 p_ply_property property;
140 long nproperties;
141 } t_ply_element;
142
143 /* ----------------------------------------------------------------------
144 * Input/output driver
145 *
146 * Depending on file mode, different functions are used to read/write
147 * property fields. The drivers make it transparent to read/write in ascii,
148 * big endian or little endian cases.
149 * ---------------------------------------------------------------------- */
150 typedef int (*p_ply_ihandler)(p_ply ply, double *value);
151 typedef int (*p_ply_ichunk)(p_ply ply, void *anydata, size_t size);
152 typedef struct t_ply_idriver_ {
153 p_ply_ihandler ihandler[16];
154 p_ply_ichunk ichunk;
155 const char *name;
156 } t_ply_idriver;
157 typedef t_ply_idriver *p_ply_idriver;
158
159 typedef int (*p_ply_ohandler)(p_ply ply, double value);
160 typedef int (*p_ply_ochunk)(p_ply ply, void *anydata, size_t size);
161 typedef struct t_ply_odriver_ {
162 p_ply_ohandler ohandler[16];
163 p_ply_ochunk ochunk;
164 const char *name;
165 } t_ply_odriver;
166 typedef t_ply_odriver *p_ply_odriver;
167
168 /* ----------------------------------------------------------------------
169 * Ply file handle.
170 *
171 * io_mode: read or write (from e_ply_io_mode)
172 * storage_mode: mode of file associated with handle (from e_ply_storage_mode)
173 * element: elements description for this file
174 * nelement: number of different elements in file
175 * comment: comments for this file
176 * ncomments: number of comments in file
177 * obj_info: obj_info items for this file
178 * nobj_infos: number of obj_info items in file
179 * fp: file pointer associated with ply file
180 * rn: skip extra char after end_header?
181 * buffer: last word/chunck of data read from ply file
182 * buffer_first, buffer_last: interval of untouched good data in buffer
183 * buffer_token: start of parsed token (line or word) in buffer
184 * idriver, odriver: input driver used to get property fields from file
185 * argument: storage space for callback arguments
186 * welement, wproperty: element/property type being written
187 * winstance_index: index of instance of current element being written
188 * wvalue_index: index of list property value being written
189 * wlength: number of values in list property being written
190 * error_cb: error callback
191 * pdata/idata: user data defined with ply_open/ply_create
192 * ---------------------------------------------------------------------- */
193 typedef struct t_ply_ {
194 e_ply_io_mode io_mode;
195 e_ply_storage_mode storage_mode;
196 p_ply_element element;
197 long nelements;
198 char *comment;
199 long ncomments;
200 char *obj_info;
201 long nobj_infos;
202 FILE *fp;
203 int rn;
204 char buffer[BUFFERSIZE];
205 size_t buffer_first, buffer_token, buffer_last;
206 p_ply_idriver idriver;
207 p_ply_odriver odriver;
208 t_ply_argument argument;
209 long welement, wproperty;
210 long winstance_index, wvalue_index, wlength;
211 p_ply_error_cb error_cb;
212 void *pdata;
213 long idata;
214 } t_ply;
215
216 /* ----------------------------------------------------------------------
217 * I/O functions and drivers
218 * ---------------------------------------------------------------------- */
219 static t_ply_idriver ply_idriver_ascii;
220 static t_ply_idriver ply_idriver_binary;
221 static t_ply_idriver ply_idriver_binary_reverse;
222 static t_ply_odriver ply_odriver_ascii;
223 static t_ply_odriver ply_odriver_binary;
224 static t_ply_odriver ply_odriver_binary_reverse;
225
226 static int ply_read_word(p_ply ply);
227 static int ply_check_word(p_ply ply);
228 static void ply_finish_word(p_ply ply, size_t size);
229 static int ply_read_line(p_ply ply);
230 static int ply_check_line(p_ply ply);
231 static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size);
232 static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size);
233 static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size);
234 static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size);
235 static void ply_reverse(void *anydata, size_t size);
236
237 /* ----------------------------------------------------------------------
238 * String functions
239 * ---------------------------------------------------------------------- */
240 static int ply_find_string(const char *item, const char* const list[]);
241 static p_ply_element ply_find_element(p_ply ply, const char *name);
242 static p_ply_property ply_find_property(p_ply_element element,
243 const char *name);
244
245 /* ----------------------------------------------------------------------
246 * Header parsing
247 * ---------------------------------------------------------------------- */
248 static int ply_read_header_magic(p_ply ply);
249 static int ply_read_header_format(p_ply ply);
250 static int ply_read_header_comment(p_ply ply);
251 static int ply_read_header_obj_info(p_ply ply);
252 static int ply_read_header_property(p_ply ply);
253 static int ply_read_header_element(p_ply ply);
254
255 /* ----------------------------------------------------------------------
256 * Error handling
257 * ---------------------------------------------------------------------- */
258 static void ply_error_cb(p_ply ply, const char *message);
259 static void ply_ferror(p_ply ply, const char *fmt, ...);
260
261 /* ----------------------------------------------------------------------
262 * Memory allocation and initialization
263 * ---------------------------------------------------------------------- */
264 static void ply_init(p_ply ply);
265 static void ply_element_init(p_ply_element element);
266 static void ply_property_init(p_ply_property property);
267 static p_ply ply_alloc(void);
268 static p_ply_element ply_grow_element(p_ply ply);
269 static p_ply_property ply_grow_property(p_ply ply, p_ply_element element);
270 static void *ply_grow_array(p_ply ply, void **pointer, long *nmemb, long size);
271
272 /* ----------------------------------------------------------------------
273 * Special functions
274 * ---------------------------------------------------------------------- */
275 static e_ply_storage_mode ply_arch_endian(void);
276 static int ply_type_check(void);
277
278 /* ----------------------------------------------------------------------
279 * Auxiliary read functions
280 * ---------------------------------------------------------------------- */
281 static int ply_read_element(p_ply ply, p_ply_element element,
282 p_ply_argument argument);
283 static int ply_read_property(p_ply ply, p_ply_element element,
284 p_ply_property property, p_ply_argument argument);
285 static int ply_read_list_property(p_ply ply, p_ply_element element,
286 p_ply_property property, p_ply_argument argument);
287 static int ply_read_scalar_property(p_ply ply, p_ply_element element,
288 p_ply_property property, p_ply_argument argument);
289
290 /* ----------------------------------------------------------------------
291 * Buffer support functions
292 * ---------------------------------------------------------------------- */
293 /* pointers to tokenized word and line in buffer */
294 #define BWORD(p) (p->buffer + p->buffer_token)
295 #define BLINE(p) (p->buffer + p->buffer_token)
296
297 /* pointer to start of untouched bytes in buffer */
298 #define BFIRST(p) (p->buffer + p->buffer_first)
299
300 /* number of bytes untouched in buffer */
301 #define BSIZE(p) (p->buffer_last - p->buffer_first)
302
303 /* consumes data from buffer */
304 #define BSKIP(p, s) (p->buffer_first += s)
305
306 /* refills the buffer */
BREFILL(p_ply ply)307 static int BREFILL(p_ply ply) {
308 /* move untouched data to beginning of buffer */
309 size_t size = BSIZE(ply);
310 memmove(ply->buffer, BFIRST(ply), size);
311 ply->buffer_last = size;
312 ply->buffer_first = ply->buffer_token = 0;
313 /* fill remaining with new data */
314 size = fread(ply->buffer+size, 1, BUFFERSIZE-size-1, ply->fp);
315 /* place sentinel so we can use str* functions with buffer */
316 ply->buffer[BUFFERSIZE-1] = '\0';
317 /* check if read failed */
318 if (size <= 0) return 0;
319 /* increase size to account for new data */
320 ply->buffer_last += size;
321 return 1;
322 }
323
324 /* We don't care about end-of-line, generally, because we
325 * separate words by any white-space character.
326 * Unfortunately, in binary mode, right after 'end_header',
327 * we have to know *exactly* how many characters to skip */
328 /* We use the end-of-line marker after the 'ply' magic
329 * number to figure out what to do */
ply_read_header_magic(p_ply ply)330 static int ply_read_header_magic(p_ply ply) {
331 char *magic = ply->buffer;
332 if (!BREFILL(ply)) {
333 ply->error_cb(ply, "Unable to read magic number from file");
334 return 0;
335 }
336 /* check if it is ply */
337 if (magic[0] != 'p' || magic[1] != 'l' || magic[2] != 'y'
338 || !isspace(magic[3])) {
339 ply->error_cb(ply, "Wrong magic number. Expected 'ply'");
340 return 0;
341 }
342 /* figure out if we have to skip the extra character
343 * after header when we reach the binary part of file */
344 ply->rn = magic[3] == '\r' && magic[4] == '\n';
345 BSKIP(ply, 3);
346 return 1;
347 }
348
349 /* ----------------------------------------------------------------------
350 * Exported functions
351 * ---------------------------------------------------------------------- */
352 /* ----------------------------------------------------------------------
353 * Read support functions
354 * ---------------------------------------------------------------------- */
ply_open(const char * name,p_ply_error_cb error_cb,long idata,void * pdata)355 p_ply ply_open(const char *name, p_ply_error_cb error_cb,
356 long idata, void *pdata) {
357 FILE *fp = NULL;
358 p_ply ply = ply_alloc();
359 if (error_cb == NULL) error_cb = ply_error_cb;
360 if (!ply) {
361 error_cb(NULL, "Out of memory");
362 return NULL;
363 }
364 ply->idata = idata;
365 ply->pdata = pdata;
366 ply->io_mode = PLY_READ;
367 ply->error_cb = error_cb;
368 if (!ply_type_check()) {
369 error_cb(ply, "Incompatible type system");
370 free(ply);
371 return NULL;
372 }
373 assert(name);
374 fp = fopen(name, "rb");
375 if (!fp) {
376 error_cb(ply, "Unable to open file");
377 free(ply);
378 return NULL;
379 }
380 ply->fp = fp;
381 return ply;
382 }
383
ply_read_header(p_ply ply)384 int ply_read_header(p_ply ply) {
385 assert(ply && ply->fp && ply->io_mode == PLY_READ);
386 if (!ply_read_header_magic(ply)) return 0;
387 if (!ply_read_word(ply)) return 0;
388 /* parse file format */
389 if (!ply_read_header_format(ply)) {
390 ply_ferror(ply, "Invalid file format");
391 return 0;
392 }
393 /* parse elements, comments or obj_infos until the end of header */
394 while (strcmp(BWORD(ply), "end_header")) {
395 if (!ply_read_header_comment(ply) &&
396 !ply_read_header_element(ply) &&
397 !ply_read_header_obj_info(ply)) {
398 ply_ferror(ply, "Unexpected token '%s'", BWORD(ply));
399 return 0;
400 }
401 }
402 /* skip extra character? */
403 if (ply->rn) {
404 if (BSIZE(ply) < 1 && !BREFILL(ply)) {
405 ply_ferror(ply, "Unexpected end of file");
406 return 0;
407 }
408 BSKIP(ply, 1);
409 }
410 return 1;
411 }
412
ply_set_read_cb(p_ply ply,const char * element_name,const char * property_name,p_ply_read_cb read_cb,void * pdata,long idata)413 long ply_set_read_cb(p_ply ply, const char *element_name,
414 const char* property_name, p_ply_read_cb read_cb,
415 void *pdata, long idata) {
416 p_ply_element element = NULL;
417 p_ply_property property = NULL;
418 assert(ply && element_name && property_name);
419 element = ply_find_element(ply, element_name);
420 if (!element) return 0;
421 property = ply_find_property(element, property_name);
422 if (!property) return 0;
423 property->read_cb = read_cb;
424 property->pdata = pdata;
425 property->idata = idata;
426 return (int) element->ninstances;
427 }
428
ply_read(p_ply ply)429 int ply_read(p_ply ply) {
430 long i;
431 p_ply_argument argument;
432 assert(ply && ply->fp && ply->io_mode == PLY_READ);
433 argument = &ply->argument;
434 /* for each element type */
435 for (i = 0; i < ply->nelements; i++) {
436 p_ply_element element = &ply->element[i];
437 argument->element = element;
438 if (!ply_read_element(ply, element, argument))
439 return 0;
440 }
441 return 1;
442 }
443
444 /* ----------------------------------------------------------------------
445 * Write support functions
446 * ---------------------------------------------------------------------- */
ply_create(const char * name,e_ply_storage_mode storage_mode,p_ply_error_cb error_cb,long idata,void * pdata)447 p_ply ply_create(const char *name, e_ply_storage_mode storage_mode,
448 p_ply_error_cb error_cb, long idata, void *pdata) {
449 FILE *fp = NULL;
450 p_ply ply = ply_alloc();
451 if (error_cb == NULL) error_cb = ply_error_cb;
452 if (!ply) {
453 error_cb(NULL, "Out of memory");
454 return NULL;
455 }
456 if (!ply_type_check()) {
457 error_cb(ply, "Incompatible type system");
458 free(ply);
459 return NULL;
460 }
461 assert(name && storage_mode <= PLY_DEFAULT);
462 fp = fopen(name, "wb");
463 if (!fp) {
464 error_cb(ply, "Unable to create file");
465 free(ply);
466 return NULL;
467 }
468 ply->idata = idata;
469 ply->pdata = pdata;
470 ply->io_mode = PLY_WRITE;
471 if (storage_mode == PLY_DEFAULT) storage_mode = ply_arch_endian();
472 if (storage_mode == PLY_ASCII) ply->odriver = &ply_odriver_ascii;
473 else if (storage_mode == ply_arch_endian())
474 ply->odriver = &ply_odriver_binary;
475 else ply->odriver = &ply_odriver_binary_reverse;
476 ply->storage_mode = storage_mode;
477 ply->fp = fp;
478 ply->error_cb = error_cb;
479 return ply;
480 }
481
ply_add_element(p_ply ply,const char * name,long ninstances)482 int ply_add_element(p_ply ply, const char *name, long ninstances) {
483 p_ply_element element = NULL;
484 assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
485 assert(name && strlen(name) < WORDSIZE && ninstances >= 0);
486 if (strlen(name) >= WORDSIZE || ninstances < 0) {
487 ply_ferror(ply, "Invalid arguments");
488 return 0;
489 }
490 element = ply_grow_element(ply);
491 if (!element) return 0;
492 strcpy(element->name, name);
493 element->ninstances = ninstances;
494 return 1;
495 }
496
ply_add_scalar_property(p_ply ply,const char * name,e_ply_type type)497 int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type) {
498 p_ply_element element = NULL;
499 p_ply_property property = NULL;
500 assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
501 assert(name && strlen(name) < WORDSIZE);
502 assert(type < PLY_LIST);
503 if (strlen(name) >= WORDSIZE || type >= PLY_LIST) {
504 ply_ferror(ply, "Invalid arguments");
505 return 0;
506 }
507 element = &ply->element[ply->nelements-1];
508 property = ply_grow_property(ply, element);
509 if (!property) return 0;
510 strcpy(property->name, name);
511 property->type = type;
512 return 1;
513 }
514
ply_add_list_property(p_ply ply,const char * name,e_ply_type length_type,e_ply_type value_type)515 int ply_add_list_property(p_ply ply, const char *name,
516 e_ply_type length_type, e_ply_type value_type) {
517 p_ply_element element = NULL;
518 p_ply_property property = NULL;
519 assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
520 assert(name && strlen(name) < WORDSIZE);
521 if (strlen(name) >= WORDSIZE) {
522 ply_ferror(ply, "Invalid arguments");
523 return 0;
524 }
525 assert(length_type < PLY_LIST);
526 assert(value_type < PLY_LIST);
527 if (length_type >= PLY_LIST || value_type >= PLY_LIST) {
528 ply_ferror(ply, "Invalid arguments");
529 return 0;
530 }
531 element = &ply->element[ply->nelements-1];
532 property = ply_grow_property(ply, element);
533 if (!property) return 0;
534 strcpy(property->name, name);
535 property->type = PLY_LIST;
536 property->length_type = length_type;
537 property->value_type = value_type;
538 return 1;
539 }
540
ply_add_property(p_ply ply,const char * name,e_ply_type type,e_ply_type length_type,e_ply_type value_type)541 int ply_add_property(p_ply ply, const char *name, e_ply_type type,
542 e_ply_type length_type, e_ply_type value_type) {
543 if (type == PLY_LIST)
544 return ply_add_list_property(ply, name, length_type, value_type);
545 else
546 return ply_add_scalar_property(ply, name, type);
547 }
548
ply_add_comment(p_ply ply,const char * comment)549 int ply_add_comment(p_ply ply, const char *comment) {
550 char *new_comment = NULL;
551 assert(ply && comment && strlen(comment) < LINESIZE);
552 if (!comment || strlen(comment) >= LINESIZE) {
553 ply_ferror(ply, "Invalid arguments");
554 return 0;
555 }
556 new_comment = (char *) ply_grow_array(ply, (void **) &ply->comment,
557 &ply->ncomments, LINESIZE);
558 if (!new_comment) return 0;
559 strcpy(new_comment, comment);
560 return 1;
561 }
562
ply_add_obj_info(p_ply ply,const char * obj_info)563 int ply_add_obj_info(p_ply ply, const char *obj_info) {
564 char *new_obj_info = NULL;
565 assert(ply && obj_info && strlen(obj_info) < LINESIZE);
566 if (!obj_info || strlen(obj_info) >= LINESIZE) {
567 ply_ferror(ply, "Invalid arguments");
568 return 0;
569 }
570 new_obj_info = (char *) ply_grow_array(ply, (void **) &ply->obj_info,
571 &ply->nobj_infos, LINESIZE);
572 if (!new_obj_info) return 0;
573 strcpy(new_obj_info, obj_info);
574 return 1;
575 }
576
ply_write_header(p_ply ply)577 int ply_write_header(p_ply ply) {
578 long i, j;
579 assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
580 assert(ply->element || ply->nelements == 0);
581 assert(!ply->element || ply->nelements > 0);
582 if (fprintf(ply->fp, "ply\nformat %s 1.0\n",
583 ply_storage_mode_list[ply->storage_mode]) <= 0) goto error;
584 for (i = 0; i < ply->ncomments; i++)
585 if (fprintf(ply->fp, "comment %s\n", ply->comment + LINESIZE*i) <= 0)
586 goto error;
587 for (i = 0; i < ply->nobj_infos; i++)
588 if (fprintf(ply->fp, "obj_info %s\n", ply->obj_info + LINESIZE*i) <= 0)
589 goto error;
590 for (i = 0; i < ply->nelements; i++) {
591 p_ply_element element = &ply->element[i];
592 assert(element->property || element->nproperties == 0);
593 assert(!element->property || element->nproperties > 0);
594 if (fprintf(ply->fp, "element %s %ld\n", element->name,
595 element->ninstances) <= 0) goto error;
596 for (j = 0; j < element->nproperties; j++) {
597 p_ply_property property = &element->property[j];
598 if (property->type == PLY_LIST) {
599 if (fprintf(ply->fp, "property list %s %s %s\n",
600 ply_type_list[property->length_type],
601 ply_type_list[property->value_type],
602 property->name) <= 0) goto error;
603 } else {
604 if (fprintf(ply->fp, "property %s %s\n",
605 ply_type_list[property->type],
606 property->name) <= 0) goto error;
607 }
608 }
609 }
610 return fprintf(ply->fp, "end_header\n") > 0;
611 error:
612 ply_ferror(ply, "Error writing to file");
613 return 0;
614 }
615
ply_write(p_ply ply,double value)616 int ply_write(p_ply ply, double value) {
617 p_ply_element element = NULL;
618 p_ply_property property = NULL;
619 int type = -1;
620 int breakafter = 0;
621 int spaceafter = 1;
622 if (ply->welement > ply->nelements) return 0;
623 element = &ply->element[ply->welement];
624 if (ply->wproperty > element->nproperties) return 0;
625 property = &element->property[ply->wproperty];
626 if (property->type == PLY_LIST) {
627 if (ply->wvalue_index == 0) {
628 type = property->length_type;
629 ply->wlength = (long) value;
630 } else type = property->value_type;
631 } else {
632 type = property->type;
633 ply->wlength = 0;
634 }
635 if (!ply->odriver->ohandler[type](ply, value)) {
636 ply_ferror(ply, "Failed writing %s of %s %d (%s: %s)",
637 property->name, element->name,
638 ply->winstance_index,
639 ply->odriver->name, ply_type_list[type]);
640 return 0;
641 }
642 ply->wvalue_index++;
643 if (ply->wvalue_index > ply->wlength) {
644 ply->wvalue_index = 0;
645 ply->wproperty++;
646 }
647 if (ply->wproperty >= element->nproperties) {
648 ply->wproperty = 0;
649 ply->winstance_index++;
650 breakafter = 1;
651 spaceafter = 0;
652 }
653 if (ply->winstance_index >= element->ninstances) {
654 ply->winstance_index = 0;
655 do {
656 ply->welement++;
657 element = &ply->element[ply->welement];
658 } while (ply->welement < ply->nelements && !element->ninstances);
659 }
660 if (ply->storage_mode == PLY_ASCII) {
661 return (!spaceafter || putc(' ', ply->fp) > 0) &&
662 (!breakafter || putc('\n', ply->fp) > 0);
663 } else {
664 return 1;
665 }
666 }
667
ply_close(p_ply ply)668 int ply_close(p_ply ply) {
669 long i;
670 assert(ply && ply->fp);
671 assert(ply->element || ply->nelements == 0);
672 assert(!ply->element || ply->nelements > 0);
673 /* write last chunk to file */
674 if (ply->io_mode == PLY_WRITE &&
675 fwrite(ply->buffer, 1, ply->buffer_last, ply->fp) < ply->buffer_last) {
676 ply_ferror(ply, "Error closing up");
677 return 0;
678 }
679 fclose(ply->fp);
680 /* free all memory used by handle */
681 if (ply->element) {
682 for (i = 0; i < ply->nelements; i++) {
683 p_ply_element element = &ply->element[i];
684 if (element->property) free(element->property);
685 }
686 free(ply->element);
687 }
688 if (ply->obj_info) free(ply->obj_info);
689 if (ply->comment) free(ply->comment);
690 free(ply);
691 return 1;
692 }
693
694 /* ----------------------------------------------------------------------
695 * Query support functions
696 * ---------------------------------------------------------------------- */
ply_get_next_element(p_ply ply,p_ply_element last)697 p_ply_element ply_get_next_element(p_ply ply,
698 p_ply_element last) {
699 assert(ply);
700 if (!last) return ply->element;
701 last++;
702 if (last < ply->element + ply->nelements) return last;
703 else return NULL;
704 }
705
ply_get_element_info(p_ply_element element,const char ** name,long * ninstances)706 int ply_get_element_info(p_ply_element element, const char** name,
707 long *ninstances) {
708 assert(element);
709 if (name) *name = element->name;
710 if (ninstances) *ninstances = (long) element->ninstances;
711 return 1;
712 }
713
ply_get_next_property(p_ply_element element,p_ply_property last)714 p_ply_property ply_get_next_property(p_ply_element element,
715 p_ply_property last) {
716 assert(element);
717 if (!last) return element->property;
718 last++;
719 if (last < element->property + element->nproperties) return last;
720 else return NULL;
721 }
722
ply_get_property_info(p_ply_property property,const char ** name,e_ply_type * type,e_ply_type * length_type,e_ply_type * value_type)723 int ply_get_property_info(p_ply_property property, const char** name,
724 e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type) {
725 assert(property);
726 if (name) *name = property->name;
727 if (type) *type = property->type;
728 if (length_type) *length_type = property->length_type;
729 if (value_type) *value_type = property->value_type;
730 return 1;
731
732 }
733
ply_get_next_comment(p_ply ply,const char * last)734 const char *ply_get_next_comment(p_ply ply, const char *last) {
735 assert(ply);
736 if (!last) return ply->comment;
737 last += LINESIZE;
738 if (last < ply->comment + LINESIZE*ply->ncomments) return last;
739 else return NULL;
740 }
741
ply_get_next_obj_info(p_ply ply,const char * last)742 const char *ply_get_next_obj_info(p_ply ply, const char *last) {
743 assert(ply);
744 if (!last) return ply->obj_info;
745 last += LINESIZE;
746 if (last < ply->obj_info + LINESIZE*ply->nobj_infos) return last;
747 else return NULL;
748 }
749
750 /* ----------------------------------------------------------------------
751 * Callback argument support functions
752 * ---------------------------------------------------------------------- */
ply_get_argument_element(p_ply_argument argument,p_ply_element * element,long * instance_index)753 int ply_get_argument_element(p_ply_argument argument,
754 p_ply_element *element, long *instance_index) {
755 assert(argument);
756 if (!argument) return 0;
757 if (element) *element = argument->element;
758 if (instance_index) *instance_index = argument->instance_index;
759 return 1;
760 }
761
ply_get_argument_property(p_ply_argument argument,p_ply_property * property,long * length,long * value_index)762 int ply_get_argument_property(p_ply_argument argument,
763 p_ply_property *property, long *length, long *value_index) {
764 assert(argument);
765 if (!argument) return 0;
766 if (property) *property = argument->property;
767 if (length) *length = argument->length;
768 if (value_index) *value_index = argument->value_index;
769 return 1;
770 }
771
ply_get_argument_user_data(p_ply_argument argument,void ** pdata,long * idata)772 int ply_get_argument_user_data(p_ply_argument argument, void **pdata,
773 long *idata) {
774 assert(argument);
775 if (!argument) return 0;
776 if (pdata) *pdata = argument->pdata;
777 if (idata) *idata = argument->idata;
778 return 1;
779 }
780
ply_get_argument_value(p_ply_argument argument)781 double ply_get_argument_value(p_ply_argument argument) {
782 assert(argument);
783 if (!argument) return 0.0;
784 return argument->value;
785 }
786
ply_get_ply_user_data(p_ply ply,void ** pdata,long * idata)787 int ply_get_ply_user_data(p_ply ply, void **pdata, long *idata) {
788 assert(ply);
789 if (!ply) return 0;
790 if (pdata) *pdata = ply->pdata;
791 if (idata) *idata = ply->idata;
792 return 1;
793 }
794
795 /* ----------------------------------------------------------------------
796 * Internal functions
797 * ---------------------------------------------------------------------- */
ply_read_list_property(p_ply ply,p_ply_element element,p_ply_property property,p_ply_argument argument)798 static int ply_read_list_property(p_ply ply, p_ply_element element,
799 p_ply_property property, p_ply_argument argument) {
800 int l;
801 p_ply_read_cb read_cb = property->read_cb;
802 p_ply_ihandler *driver = ply->idriver->ihandler;
803 /* get list length */
804 p_ply_ihandler handler = driver[property->length_type];
805 double length;
806 if (!handler(ply, &length)) {
807 ply_ferror(ply, "Error reading '%s' of '%s' number %d",
808 property->name, element->name, argument->instance_index);
809 return 0;
810 }
811 /* invoke callback to pass length in value field */
812 argument->length = (long) length;
813 argument->value_index = -1;
814 argument->value = length;
815 if (read_cb && !read_cb(argument)) {
816 ply_ferror(ply, "Aborted by user");
817 return 0;
818 }
819 /* read list values */
820 handler = driver[property->value_type];
821 /* for each value in list */
822 for (l = 0; l < (long) length; l++) {
823 /* read value from file */
824 argument->value_index = l;
825 if (!handler(ply, &argument->value)) {
826 ply_ferror(ply, "Error reading value number %d of '%s' of "
827 "'%s' number %d", l+1, property->name,
828 element->name, argument->instance_index);
829 return 0;
830 }
831 /* invoke callback to pass value */
832 if (read_cb && !read_cb(argument)) {
833 ply_ferror(ply, "Aborted by user");
834 return 0;
835 }
836 }
837 return 1;
838 }
839
ply_read_scalar_property(p_ply ply,p_ply_element element,p_ply_property property,p_ply_argument argument)840 static int ply_read_scalar_property(p_ply ply, p_ply_element element,
841 p_ply_property property, p_ply_argument argument) {
842 p_ply_read_cb read_cb = property->read_cb;
843 p_ply_ihandler *driver = ply->idriver->ihandler;
844 p_ply_ihandler handler = driver[property->type];
845 argument->length = 1;
846 argument->value_index = 0;
847 if (!handler(ply, &argument->value)) {
848 ply_ferror(ply, "Error reading '%s' of '%s' number %d",
849 property->name, element->name, argument->instance_index);
850 return 0;
851 }
852 if (read_cb && !read_cb(argument)) {
853 ply_ferror(ply, "Aborted by user");
854 return 0;
855 }
856 return 1;
857 }
858
ply_read_property(p_ply ply,p_ply_element element,p_ply_property property,p_ply_argument argument)859 static int ply_read_property(p_ply ply, p_ply_element element,
860 p_ply_property property, p_ply_argument argument) {
861 if (property->type == PLY_LIST)
862 return ply_read_list_property(ply, element, property, argument);
863 else
864 return ply_read_scalar_property(ply, element, property, argument);
865 }
866
ply_read_element(p_ply ply,p_ply_element element,p_ply_argument argument)867 static int ply_read_element(p_ply ply, p_ply_element element,
868 p_ply_argument argument) {
869 long j, k;
870 /* for each element of this type */
871 for (j = 0; j < element->ninstances; j++) {
872 argument->instance_index = j;
873 /* for each property */
874 for (k = 0; k < element->nproperties; k++) {
875 p_ply_property property = &element->property[k];
876 argument->property = property;
877 argument->pdata = property->pdata;
878 argument->idata = property->idata;
879 if (!ply_read_property(ply, element, property, argument))
880 return 0;
881 }
882 }
883 return 1;
884 }
885
ply_find_string(const char * item,const char * const list[])886 static int ply_find_string(const char *item, const char* const list[]) {
887 int i;
888 assert(item && list);
889 for (i = 0; list[i]; i++)
890 if (!strcmp(list[i], item)) return i;
891 return -1;
892 }
893
ply_find_element(p_ply ply,const char * name)894 static p_ply_element ply_find_element(p_ply ply, const char *name) {
895 p_ply_element element;
896 int i, nelements;
897 assert(ply && name);
898 element = ply->element;
899 nelements = ply->nelements;
900 assert(element || nelements == 0);
901 assert(!element || nelements > 0);
902 for (i = 0; i < nelements; i++)
903 if (!strcmp(element[i].name, name)) return &element[i];
904 return NULL;
905 }
906
ply_find_property(p_ply_element element,const char * name)907 static p_ply_property ply_find_property(p_ply_element element,
908 const char *name) {
909 p_ply_property property;
910 int i, nproperties;
911 assert(element && name);
912 property = element->property;
913 nproperties = element->nproperties;
914 assert(property || nproperties == 0);
915 assert(!property || nproperties > 0);
916 for (i = 0; i < nproperties; i++)
917 if (!strcmp(property[i].name, name)) return &property[i];
918 return NULL;
919 }
920
ply_check_word(p_ply ply)921 static int ply_check_word(p_ply ply) {
922 size_t size = strlen(BWORD(ply));
923 if (size >= WORDSIZE) {
924 ply_ferror(ply, "Word too long");
925 return 0;
926 } else if (size == 0) {
927 ply_ferror(ply, "Unexpected end of file");
928 return 0;
929 }
930 return 1;
931 }
932
ply_read_word(p_ply ply)933 static int ply_read_word(p_ply ply) {
934 size_t t = 0;
935 assert(ply && ply->fp && ply->io_mode == PLY_READ);
936 /* skip leading blanks */
937 while (1) {
938 t = strspn(BFIRST(ply), " \n\r\t");
939 /* check if all buffer was made of blanks */
940 if (t >= BSIZE(ply)) {
941 if (!BREFILL(ply)) {
942 ply_ferror(ply, "Unexpected end of file");
943 return 0;
944 }
945 } else break;
946 }
947 BSKIP(ply, t);
948 /* look for a space after the current word */
949 t = strcspn(BFIRST(ply), " \n\r\t");
950 /* if we didn't reach the end of the buffer, we are done */
951 if (t < BSIZE(ply)) {
952 ply_finish_word(ply, t);
953 return ply_check_word(ply);
954 }
955 /* otherwise, try to refill buffer */
956 if (!BREFILL(ply)) {
957 /* if we reached the end of file, try to do with what we have */
958 ply_finish_word(ply, t);
959 return ply_check_word(ply);
960 /* ply_ferror(ply, "Unexpected end of file"); */
961 /* return 0; */
962 }
963 /* keep looking from where we left */
964 t += strcspn(BFIRST(ply) + t, " \n\r\t");
965 /* check if the token is too large for our buffer */
966 if (t >= BSIZE(ply)) {
967 ply_ferror(ply, "Token too large");
968 return 0;
969 }
970 /* we are done */
971 ply_finish_word(ply, t);
972 return ply_check_word(ply);
973 }
974
ply_finish_word(p_ply ply,size_t size)975 static void ply_finish_word(p_ply ply, size_t size) {
976 ply->buffer_token = ply->buffer_first;
977 BSKIP(ply, size);
978 *BFIRST(ply) = '\0';
979 BSKIP(ply, 1);
980 }
981
ply_check_line(p_ply ply)982 static int ply_check_line(p_ply ply) {
983 if (strlen(BLINE(ply)) >= LINESIZE) {
984 ply_ferror(ply, "Line too long");
985 return 0;
986 }
987 return 1;
988 }
989
ply_read_line(p_ply ply)990 static int ply_read_line(p_ply ply) {
991 const char *end = NULL;
992 assert(ply && ply->fp && ply->io_mode == PLY_READ);
993 /* look for a end of line */
994 end = strchr(BFIRST(ply), '\n');
995 /* if we didn't reach the end of the buffer, we are done */
996 if (end) {
997 ply->buffer_token = ply->buffer_first;
998 BSKIP(ply, end - BFIRST(ply));
999 *BFIRST(ply) = '\0';
1000 BSKIP(ply, 1);
1001 return ply_check_line(ply);
1002 } else {
1003 end = ply->buffer + BSIZE(ply);
1004 /* otherwise, try to refill buffer */
1005 if (!BREFILL(ply)) {
1006 ply_ferror(ply, "Unexpected end of file");
1007 return 0;
1008 }
1009 }
1010 /* keep looking from where we left */
1011 end = strchr(end, '\n');
1012 /* check if the token is too large for our buffer */
1013 if (!end) {
1014 ply_ferror(ply, "Token too large");
1015 return 0;
1016 }
1017 /* we are done */
1018 ply->buffer_token = ply->buffer_first;
1019 BSKIP(ply, end - BFIRST(ply));
1020 *BFIRST(ply) = '\0';
1021 BSKIP(ply, 1);
1022 return ply_check_line(ply);
1023 }
1024
ply_read_chunk(p_ply ply,void * anybuffer,size_t size)1025 static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size) {
1026 char *buffer = (char *) anybuffer;
1027 size_t i = 0;
1028 assert(ply && ply->fp && ply->io_mode == PLY_READ);
1029 assert(ply->buffer_first <= ply->buffer_last);
1030 while (i < size) {
1031 if (ply->buffer_first < ply->buffer_last) {
1032 buffer[i] = ply->buffer[ply->buffer_first];
1033 ply->buffer_first++;
1034 i++;
1035 } else {
1036 ply->buffer_first = 0;
1037 ply->buffer_last = fread(ply->buffer, 1, BUFFERSIZE, ply->fp);
1038 if (ply->buffer_last <= 0) return 0;
1039 }
1040 }
1041 return 1;
1042 }
1043
ply_write_chunk(p_ply ply,void * anybuffer,size_t size)1044 static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size) {
1045 char *buffer = (char *) anybuffer;
1046 size_t i = 0;
1047 assert(ply && ply->fp && ply->io_mode == PLY_WRITE);
1048 assert(ply->buffer_last <= BUFFERSIZE);
1049 while (i < size) {
1050 if (ply->buffer_last < BUFFERSIZE) {
1051 ply->buffer[ply->buffer_last] = buffer[i];
1052 ply->buffer_last++;
1053 i++;
1054 } else {
1055 ply->buffer_last = 0;
1056 if (fwrite(ply->buffer, 1, BUFFERSIZE, ply->fp) < BUFFERSIZE)
1057 return 0;
1058 }
1059 }
1060 return 1;
1061 }
1062
ply_write_chunk_reverse(p_ply ply,void * anybuffer,size_t size)1063 static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size) {
1064 int ret = 0;
1065 ply_reverse(anybuffer, size);
1066 ret = ply_write_chunk(ply, anybuffer, size);
1067 ply_reverse(anybuffer, size);
1068 return ret;
1069 }
1070
ply_read_chunk_reverse(p_ply ply,void * anybuffer,size_t size)1071 static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size) {
1072 if (!ply_read_chunk(ply, anybuffer, size)) return 0;
1073 ply_reverse(anybuffer, size);
1074 return 1;
1075 }
1076
ply_reverse(void * anydata,size_t size)1077 static void ply_reverse(void *anydata, size_t size) {
1078 char *data = (char *) anydata;
1079 char temp;
1080 size_t i;
1081 for (i = 0; i < size/2; i++) {
1082 temp = data[i];
1083 data[i] = data[size-i-1];
1084 data[size-i-1] = temp;
1085 }
1086 }
1087
ply_init(p_ply ply)1088 static void ply_init(p_ply ply) {
1089 ply->element = NULL;
1090 ply->nelements = 0;
1091 ply->comment = NULL;
1092 ply->ncomments = 0;
1093 ply->obj_info = NULL;
1094 ply->nobj_infos = 0;
1095 ply->idriver = NULL;
1096 ply->odriver = NULL;
1097 ply->buffer[0] = '\0';
1098 ply->buffer_first = ply->buffer_last = ply->buffer_token = 0;
1099 ply->welement = 0;
1100 ply->wproperty = 0;
1101 ply->winstance_index = 0;
1102 ply->wlength = 0;
1103 ply->wvalue_index = 0;
1104 }
1105
ply_element_init(p_ply_element element)1106 static void ply_element_init(p_ply_element element) {
1107 element->name[0] = '\0';
1108 element->ninstances = 0;
1109 element->property = NULL;
1110 element->nproperties = 0;
1111 }
1112
ply_property_init(p_ply_property property)1113 static void ply_property_init(p_ply_property property) {
1114 property->name[0] = '\0';
1115 property->type = -1;
1116 property->length_type = -1;
1117 property->value_type = -1;
1118 property->read_cb = (p_ply_read_cb) NULL;
1119 property->pdata = NULL;
1120 property->idata = 0;
1121 }
1122
ply_alloc(void)1123 static p_ply ply_alloc(void) {
1124 p_ply ply = (p_ply) calloc(1, sizeof(t_ply));
1125 if (!ply) return NULL;
1126 ply_init(ply);
1127 return ply;
1128 }
1129
ply_grow_array(p_ply ply,void ** pointer,long * nmemb,long size)1130 static void *ply_grow_array(p_ply ply, void **pointer,
1131 long *nmemb, long size) {
1132 void *temp = *pointer;
1133 long count = *nmemb + 1;
1134 if (!temp) temp = malloc(count*size);
1135 else temp = realloc(temp, count*size);
1136 if (!temp) {
1137 ply_ferror(ply, "Out of memory");
1138 return NULL;
1139 }
1140 *pointer = temp;
1141 *nmemb = count;
1142 return (char *) temp + (count-1) * size;
1143 }
1144
ply_grow_element(p_ply ply)1145 static p_ply_element ply_grow_element(p_ply ply) {
1146 p_ply_element element = NULL;
1147 assert(ply);
1148 assert(ply->element || ply->nelements == 0);
1149 assert(!ply->element || ply->nelements > 0);
1150 element = (p_ply_element) ply_grow_array(ply, (void **) &ply->element,
1151 &ply->nelements, sizeof(t_ply_element));
1152 if (!element) return NULL;
1153 ply_element_init(element);
1154 return element;
1155 }
1156
ply_grow_property(p_ply ply,p_ply_element element)1157 static p_ply_property ply_grow_property(p_ply ply, p_ply_element element) {
1158 p_ply_property property = NULL;
1159 assert(ply);
1160 assert(element);
1161 assert(element->property || element->nproperties == 0);
1162 assert(!element->property || element->nproperties > 0);
1163 property = (p_ply_property) ply_grow_array(ply,
1164 (void **) &element->property,
1165 &element->nproperties, sizeof(t_ply_property));
1166 if (!property) return NULL;
1167 ply_property_init(property);
1168 return property;
1169 }
1170
ply_read_header_format(p_ply ply)1171 static int ply_read_header_format(p_ply ply) {
1172 assert(ply && ply->fp && ply->io_mode == PLY_READ);
1173 if (strcmp(BWORD(ply), "format")) return 0;
1174 if (!ply_read_word(ply)) return 0;
1175 ply->storage_mode = ply_find_string(BWORD(ply), ply_storage_mode_list);
1176 if (ply->storage_mode == (e_ply_storage_mode) (-1)) return 0;
1177 if (ply->storage_mode == PLY_ASCII) ply->idriver = &ply_idriver_ascii;
1178 else if (ply->storage_mode == ply_arch_endian())
1179 ply->idriver = &ply_idriver_binary;
1180 else ply->idriver = &ply_idriver_binary_reverse;
1181 if (!ply_read_word(ply)) return 0;
1182 if (strcmp(BWORD(ply), "1.0")) return 0;
1183 if (!ply_read_word(ply)) return 0;
1184 return 1;
1185 }
1186
ply_read_header_comment(p_ply ply)1187 static int ply_read_header_comment(p_ply ply) {
1188 assert(ply && ply->fp && ply->io_mode == PLY_READ);
1189 if (strcmp(BWORD(ply), "comment")) return 0;
1190 if (!ply_read_line(ply)) return 0;
1191 if (!ply_add_comment(ply, BLINE(ply))) return 0;
1192 if (!ply_read_word(ply)) return 0;
1193 return 1;
1194 }
1195
ply_read_header_obj_info(p_ply ply)1196 static int ply_read_header_obj_info(p_ply ply) {
1197 assert(ply && ply->fp && ply->io_mode == PLY_READ);
1198 if (strcmp(BWORD(ply), "obj_info")) return 0;
1199 if (!ply_read_line(ply)) return 0;
1200 if (!ply_add_obj_info(ply, BLINE(ply))) return 0;
1201 if (!ply_read_word(ply)) return 0;
1202 return 1;
1203 }
1204
ply_read_header_property(p_ply ply)1205 static int ply_read_header_property(p_ply ply) {
1206 p_ply_element element = NULL;
1207 p_ply_property property = NULL;
1208 /* make sure it is a property */
1209 if (strcmp(BWORD(ply), "property")) return 0;
1210 element = &ply->element[ply->nelements-1];
1211 property = ply_grow_property(ply, element);
1212 if (!property) return 0;
1213 /* get property type */
1214 if (!ply_read_word(ply)) return 0;
1215 property->type = ply_find_string(BWORD(ply), ply_type_list);
1216 if (property->type == (e_ply_type) (-1)) return 0;
1217 if (property->type == PLY_LIST) {
1218 /* if it's a list, we need the base types */
1219 if (!ply_read_word(ply)) return 0;
1220 property->length_type = ply_find_string(BWORD(ply), ply_type_list);
1221 if (property->length_type == (e_ply_type) (-1)) return 0;
1222 if (!ply_read_word(ply)) return 0;
1223 property->value_type = ply_find_string(BWORD(ply), ply_type_list);
1224 if (property->value_type == (e_ply_type) (-1)) return 0;
1225 }
1226 /* get property name */
1227 if (!ply_read_word(ply)) return 0;
1228 strcpy(property->name, BWORD(ply));
1229 if (!ply_read_word(ply)) return 0;
1230 return 1;
1231 }
1232
ply_read_header_element(p_ply ply)1233 static int ply_read_header_element(p_ply ply) {
1234 p_ply_element element = NULL;
1235 long dummy;
1236 assert(ply && ply->fp && ply->io_mode == PLY_READ);
1237 if (strcmp(BWORD(ply), "element")) return 0;
1238 /* allocate room for new element */
1239 element = ply_grow_element(ply);
1240 if (!element) return 0;
1241 /* get element name */
1242 if (!ply_read_word(ply)) return 0;
1243 strcpy(element->name, BWORD(ply));
1244 /* get number of elements of this type */
1245 if (!ply_read_word(ply)) return 0;
1246 if (sscanf(BWORD(ply), "%ld", &dummy) != 1) {
1247 ply_ferror(ply, "Expected number got '%s'", BWORD(ply));
1248 return 0;
1249 }
1250 element->ninstances = dummy;
1251 /* get all properties for this element */
1252 if (!ply_read_word(ply)) return 0;
1253 while (ply_read_header_property(ply) ||
1254 ply_read_header_comment(ply) || ply_read_header_obj_info(ply))
1255 /* do nothing */;
1256 return 1;
1257 }
1258
ply_error_cb(p_ply ply,const char * message)1259 static void ply_error_cb(p_ply ply, const char *message) {
1260 (void) ply;
1261 fprintf(stderr, "RPly: %s\n", message);
1262 }
1263
ply_ferror(p_ply ply,const char * fmt,...)1264 static void ply_ferror(p_ply ply, const char *fmt, ...) {
1265 char buffer[1024];
1266 va_list ap;
1267 va_start(ap, fmt);
1268 vsprintf(buffer, fmt, ap);
1269 va_end(ap);
1270 ply->error_cb(ply, buffer);
1271 }
1272
ply_arch_endian(void)1273 static e_ply_storage_mode ply_arch_endian(void) {
1274 unsigned long i = 1;
1275 unsigned char *s = (unsigned char *) &i;
1276 if (*s == 1) return PLY_LITTLE_ENDIAN;
1277 else return PLY_BIG_ENDIAN;
1278 }
1279
ply_type_check(void)1280 static int ply_type_check(void) {
1281 assert(sizeof(t_ply_int8) == 1);
1282 assert(sizeof(t_ply_uint8) == 1);
1283 assert(sizeof(t_ply_int16) == 2);
1284 assert(sizeof(t_ply_uint16) == 2);
1285 assert(sizeof(t_ply_int32) == 4);
1286 assert(sizeof(t_ply_uint32) == 4);
1287 assert(sizeof(float) == 4);
1288 assert(sizeof(double) == 8);
1289 if (sizeof(t_ply_int8) != 1) return 0;
1290 if (sizeof(t_ply_uint8) != 1) return 0;
1291 if (sizeof(t_ply_int16) != 2) return 0;
1292 if (sizeof(t_ply_uint16) != 2) return 0;
1293 if (sizeof(t_ply_int32) != 4) return 0;
1294 if (sizeof(t_ply_uint32) != 4) return 0;
1295 if (sizeof(float) != 4) return 0;
1296 if (sizeof(double) != 8) return 0;
1297 return 1;
1298 }
1299
1300 /* ----------------------------------------------------------------------
1301 * Output handlers
1302 * ---------------------------------------------------------------------- */
oascii_int8(p_ply ply,double value)1303 static int oascii_int8(p_ply ply, double value) {
1304 if (value > PLY_INT8_MAX || value < PLY_INT8_MIN) return 0;
1305 return fprintf(ply->fp, "%d", (t_ply_int8) value) > 0;
1306 }
1307
oascii_uint8(p_ply ply,double value)1308 static int oascii_uint8(p_ply ply, double value) {
1309 if (value > PLY_UINT8_MAX || value < 0) return 0;
1310 return fprintf(ply->fp, "%d", (t_ply_uint8) value) > 0;
1311 }
1312
oascii_int16(p_ply ply,double value)1313 static int oascii_int16(p_ply ply, double value) {
1314 if (value > PLY_INT16_MAX || value < PLY_INT16_MIN) return 0;
1315 return fprintf(ply->fp, "%d", (t_ply_int16) value) > 0;
1316 }
1317
oascii_uint16(p_ply ply,double value)1318 static int oascii_uint16(p_ply ply, double value) {
1319 if (value > PLY_UINT16_MAX || value < 0) return 0;
1320 return fprintf(ply->fp, "%d", (t_ply_uint16) value) > 0;
1321 }
1322
oascii_int32(p_ply ply,double value)1323 static int oascii_int32(p_ply ply, double value) {
1324 if (value > PLY_INT32_MAX || value < PLY_INT32_MIN) return 0;
1325 return fprintf(ply->fp, "%d", (t_ply_int32) value) > 0;
1326 }
1327
oascii_uint32(p_ply ply,double value)1328 static int oascii_uint32(p_ply ply, double value) {
1329 if (value > PLY_UINT32_MAX || value < 0) return 0;
1330 return fprintf(ply->fp, "%d", (t_ply_uint32) value) > 0;
1331 }
1332
oascii_float32(p_ply ply,double value)1333 static int oascii_float32(p_ply ply, double value) {
1334 if (value < -FLT_MAX || value > FLT_MAX) return 0;
1335 return fprintf(ply->fp, "%g", (float) value) > 0;
1336 }
1337
oascii_float64(p_ply ply,double value)1338 static int oascii_float64(p_ply ply, double value) {
1339 if (value < -DBL_MAX || value > DBL_MAX) return 0;
1340 return fprintf(ply->fp, "%g", value) > 0;
1341 }
1342
obinary_int8(p_ply ply,double value)1343 static int obinary_int8(p_ply ply, double value) {
1344 t_ply_int8 int8 = (t_ply_int8) value;
1345 if (value > PLY_INT8_MAX || value < PLY_INT8_MIN) return 0;
1346 return ply->odriver->ochunk(ply, &int8, sizeof(int8));
1347 }
1348
obinary_uint8(p_ply ply,double value)1349 static int obinary_uint8(p_ply ply, double value) {
1350 t_ply_uint8 uint8 = (t_ply_uint8) value;
1351 if (value > PLY_UINT8_MAX || value < 0) return 0;
1352 return ply->odriver->ochunk(ply, &uint8, sizeof(uint8));
1353 }
1354
obinary_int16(p_ply ply,double value)1355 static int obinary_int16(p_ply ply, double value) {
1356 t_ply_int16 int16 = (t_ply_int16) value;
1357 if (value > PLY_INT16_MAX || value < PLY_INT16_MIN) return 0;
1358 return ply->odriver->ochunk(ply, &int16, sizeof(int16));
1359 }
1360
obinary_uint16(p_ply ply,double value)1361 static int obinary_uint16(p_ply ply, double value) {
1362 t_ply_uint16 uint16 = (t_ply_uint16) value;
1363 if (value > PLY_UINT16_MAX || value < 0) return 0;
1364 return ply->odriver->ochunk(ply, &uint16, sizeof(uint16));
1365 }
1366
obinary_int32(p_ply ply,double value)1367 static int obinary_int32(p_ply ply, double value) {
1368 t_ply_int32 int32 = (t_ply_int32) value;
1369 if (value > PLY_INT32_MAX || value < PLY_INT32_MIN) return 0;
1370 return ply->odriver->ochunk(ply, &int32, sizeof(int32));
1371 }
1372
obinary_uint32(p_ply ply,double value)1373 static int obinary_uint32(p_ply ply, double value) {
1374 t_ply_uint32 uint32 = (t_ply_uint32) value;
1375 if (value > PLY_UINT32_MAX || value < 0) return 0;
1376 return ply->odriver->ochunk(ply, &uint32, sizeof(uint32));
1377 }
1378
obinary_float32(p_ply ply,double value)1379 static int obinary_float32(p_ply ply, double value) {
1380 float float32 = (float) value;
1381 if (value > FLT_MAX || value < -FLT_MAX) return 0;
1382 return ply->odriver->ochunk(ply, &float32, sizeof(float32));
1383 }
1384
obinary_float64(p_ply ply,double value)1385 static int obinary_float64(p_ply ply, double value) {
1386 return ply->odriver->ochunk(ply, &value, sizeof(value));
1387 }
1388
1389 /* ----------------------------------------------------------------------
1390 * Input handlers
1391 * ---------------------------------------------------------------------- */
iascii_int8(p_ply ply,double * value)1392 static int iascii_int8(p_ply ply, double *value) {
1393 char *end;
1394 if (!ply_read_word(ply)) return 0;
1395 *value = strtol(BWORD(ply), &end, 10);
1396 if (*end || *value > PLY_INT8_MAX || *value < PLY_INT8_MIN) return 0;
1397 return 1;
1398 }
1399
iascii_uint8(p_ply ply,double * value)1400 static int iascii_uint8(p_ply ply, double *value) {
1401 char *end;
1402 if (!ply_read_word(ply)) return 0;
1403 *value = strtol(BWORD(ply), &end, 10);
1404 if (*end || *value > PLY_UINT8_MAX || *value < 0) return 0;
1405 return 1;
1406 }
1407
iascii_int16(p_ply ply,double * value)1408 static int iascii_int16(p_ply ply, double *value) {
1409 char *end;
1410 if (!ply_read_word(ply)) return 0;
1411 *value = strtol(BWORD(ply), &end, 10);
1412 if (*end || *value > PLY_INT16_MAX || *value < PLY_INT16_MIN) return 0;
1413 return 1;
1414 }
1415
iascii_uint16(p_ply ply,double * value)1416 static int iascii_uint16(p_ply ply, double *value) {
1417 char *end;
1418 if (!ply_read_word(ply)) return 0;
1419 *value = strtol(BWORD(ply), &end, 10);
1420 if (*end || *value > PLY_UINT16_MAX || *value < 0) return 0;
1421 return 1;
1422 }
1423
iascii_int32(p_ply ply,double * value)1424 static int iascii_int32(p_ply ply, double *value) {
1425 char *end;
1426 if (!ply_read_word(ply)) return 0;
1427 *value = strtol(BWORD(ply), &end, 10);
1428 if (*end || *value > PLY_INT32_MAX || *value < PLY_INT32_MIN) return 0;
1429 return 1;
1430 }
1431
iascii_uint32(p_ply ply,double * value)1432 static int iascii_uint32(p_ply ply, double *value) {
1433 char *end;
1434 if (!ply_read_word(ply)) return 0;
1435 *value = strtol(BWORD(ply), &end, 10);
1436 if (*end || *value > PLY_UINT32_MAX || *value < 0) return 0;
1437 return 1;
1438 }
1439
iascii_float32(p_ply ply,double * value)1440 static int iascii_float32(p_ply ply, double *value) {
1441 char *end;
1442 if (!ply_read_word(ply)) return 0;
1443 *value = strtod(BWORD(ply), &end);
1444 if (*end || *value < -FLT_MAX || *value > FLT_MAX) return 0;
1445 return 1;
1446 }
1447
iascii_float64(p_ply ply,double * value)1448 static int iascii_float64(p_ply ply, double *value) {
1449 char *end;
1450 if (!ply_read_word(ply)) return 0;
1451 *value = strtod(BWORD(ply), &end);
1452 if (*end || *value < -DBL_MAX || *value > DBL_MAX) return 0;
1453 return 1;
1454 }
1455
ibinary_int8(p_ply ply,double * value)1456 static int ibinary_int8(p_ply ply, double *value) {
1457 t_ply_int8 int8;
1458 if (!ply->idriver->ichunk(ply, &int8, 1)) return 0;
1459 *value = int8;
1460 return 1;
1461 }
1462
ibinary_uint8(p_ply ply,double * value)1463 static int ibinary_uint8(p_ply ply, double *value) {
1464 t_ply_uint8 uint8;
1465 if (!ply->idriver->ichunk(ply, &uint8, 1)) return 0;
1466 *value = uint8;
1467 return 1;
1468 }
1469
ibinary_int16(p_ply ply,double * value)1470 static int ibinary_int16(p_ply ply, double *value) {
1471 t_ply_int16 int16;
1472 if (!ply->idriver->ichunk(ply, &int16, sizeof(int16))) return 0;
1473 *value = int16;
1474 return 1;
1475 }
1476
ibinary_uint16(p_ply ply,double * value)1477 static int ibinary_uint16(p_ply ply, double *value) {
1478 t_ply_uint16 uint16;
1479 if (!ply->idriver->ichunk(ply, &uint16, sizeof(uint16))) return 0;
1480 *value = uint16;
1481 return 1;
1482 }
1483
ibinary_int32(p_ply ply,double * value)1484 static int ibinary_int32(p_ply ply, double *value) {
1485 t_ply_int32 int32;
1486 if (!ply->idriver->ichunk(ply, &int32, sizeof(int32))) return 0;
1487 *value = int32;
1488 return 1;
1489 }
1490
ibinary_uint32(p_ply ply,double * value)1491 static int ibinary_uint32(p_ply ply, double *value) {
1492 t_ply_uint32 uint32;
1493 if (!ply->idriver->ichunk(ply, &uint32, sizeof(uint32))) return 0;
1494 *value = uint32;
1495 return 1;
1496 }
1497
ibinary_float32(p_ply ply,double * value)1498 static int ibinary_float32(p_ply ply, double *value) {
1499 float float32;
1500 if (!ply->idriver->ichunk(ply, &float32, sizeof(float32))) return 0;
1501 *value = float32;
1502 return 1;
1503 }
1504
ibinary_float64(p_ply ply,double * value)1505 static int ibinary_float64(p_ply ply, double *value) {
1506 return ply->idriver->ichunk(ply, value, sizeof(double));
1507 }
1508
1509 /* ----------------------------------------------------------------------
1510 * Constants
1511 * ---------------------------------------------------------------------- */
1512 static t_ply_idriver ply_idriver_ascii = {
1513 { iascii_int8, iascii_uint8, iascii_int16, iascii_uint16,
1514 iascii_int32, iascii_uint32, iascii_float32, iascii_float64,
1515 iascii_int8, iascii_uint8, iascii_int16, iascii_uint16,
1516 iascii_int32, iascii_uint32, iascii_float32, iascii_float64
1517 }, /* order matches e_ply_type enum */
1518 NULL,
1519 "ascii input"
1520 };
1521
1522 static t_ply_idriver ply_idriver_binary = {
1523 { ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
1524 ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64,
1525 ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
1526 ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64
1527 }, /* order matches e_ply_type enum */
1528 ply_read_chunk,
1529 "binary input"
1530 };
1531
1532 static t_ply_idriver ply_idriver_binary_reverse = {
1533 { ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
1534 ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64,
1535 ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16,
1536 ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64
1537 }, /* order matches e_ply_type enum */
1538 ply_read_chunk_reverse,
1539 "reverse binary input"
1540 };
1541
1542 static t_ply_odriver ply_odriver_ascii = {
1543 { oascii_int8, oascii_uint8, oascii_int16, oascii_uint16,
1544 oascii_int32, oascii_uint32, oascii_float32, oascii_float64,
1545 oascii_int8, oascii_uint8, oascii_int16, oascii_uint16,
1546 oascii_int32, oascii_uint32, oascii_float32, oascii_float64
1547 }, /* order matches e_ply_type enum */
1548 NULL,
1549 "ascii output"
1550 };
1551
1552 static t_ply_odriver ply_odriver_binary = {
1553 { obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
1554 obinary_int32, obinary_uint32, obinary_float32, obinary_float64,
1555 obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
1556 obinary_int32, obinary_uint32, obinary_float32, obinary_float64
1557 }, /* order matches e_ply_type enum */
1558 ply_write_chunk,
1559 "binary output"
1560 };
1561
1562 static t_ply_odriver ply_odriver_binary_reverse = {
1563 { obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
1564 obinary_int32, obinary_uint32, obinary_float32, obinary_float64,
1565 obinary_int8, obinary_uint8, obinary_int16, obinary_uint16,
1566 obinary_int32, obinary_uint32, obinary_float32, obinary_float64
1567 }, /* order matches e_ply_type enum */
1568 ply_write_chunk_reverse,
1569 "reverse binary output"
1570 };
1571
1572 /* ----------------------------------------------------------------------
1573 * Copyright (C) 2003-2011 Diego Nehab. All rights reserved.
1574 *
1575 * Permission is hereby granted, free of charge, to any person obtaining
1576 * a copy of this software and associated documentation files (the
1577 * "Software"), to deal in the Software without restriction, including
1578 * without limitation the rights to use, copy, modify, merge, publish,
1579 * distribute, sublicense, and/or sell copies of the Software, and to
1580 * permit persons to whom the Software is furnished to do so, subject to
1581 * the following conditions:
1582 *
1583 * The above copyright notice and this permission notice shall be
1584 * included in all copies or substantial portions of the Software.
1585 *
1586 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1587 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1588 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1589 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1590 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
1591 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1592 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1593 * ---------------------------------------------------------------------- */
1594