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