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