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