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