Reading Ply Model(practicing)
A. First Edition
This is almost a trivial practice of win32 api. However, I spent a half night, literally, to implement it.
My project needs a huge data model and I have to download the famous "power plant" 3D model. However, it is really insane to give windows user a big-endian format! What is the purpose of author? It cost me almost a whole night to realize this!
And if you use .c file in VC++6, the compiler will strictly use C syntax which doesn't allow variable declaration after a function call! I spent more than an hour and googled this.
This is a just personal practicing and testing for my project.
E.Further improvement
F.File listing
1. rply.h (downloaded)
2. rply.c (downloaded)
3. sconver.c (downloaded and modified a little bit)
4. main.c
file name: rply.h
#ifndef PLY_H #define PLY_H /* ---------------------------------------------------------------------- * RPly library, read/write PLY files * Diego Nehab, Princeton University * http://www.cs.princeton.edu/~diego/professional/rply * * This library is distributed under the MIT License. See notice * at the end of this file. * ---------------------------------------------------------------------- */ #ifdef __cplusplus extern "C" { #endif #define RPLY_VERSION "RPly 1.01" #define RPLY_COPYRIGHT "Copyright (C) 2003-2005 Diego Nehab" #define RPLY_AUTHORS "Diego Nehab" /* ---------------------------------------------------------------------- * Types * ---------------------------------------------------------------------- */ /* structures are opaque */ typedef struct t_ply_ *p_ply; typedef struct t_ply_element_ *p_ply_element; typedef struct t_ply_property_ *p_ply_property; typedef struct t_ply_argument_ *p_ply_argument; /* ply format mode type */ typedef enum e_ply_storage_mode_ { PLY_BIG_ENDIAN, PLY_LITTLE_ENDIAN, PLY_ASCII, PLY_DEFAULT /* has to be the last in enum */ } e_ply_storage_mode; /* order matches ply_storage_mode_list */ /* ply data type */ typedef enum e_ply_type { PLY_INT8, PLY_UINT8, PLY_INT16, PLY_UINT16, PLY_INT32, PLY_UIN32, PLY_FLOAT32, PLY_FLOAT64, PLY_CHAR, PLY_UCHAR, PLY_SHORT, PLY_USHORT, PLY_INT, PLY_UINT, PLY_FLOAT, PLY_DOUBLE, PLY_LIST /* has to be the last in enum */ } e_ply_type; /* order matches ply_type_list */ /* ---------------------------------------------------------------------- * Property reading callback prototype * * message: error message * ---------------------------------------------------------------------- */ typedef void (*p_ply_error_cb)(const char *message); /* ---------------------------------------------------------------------- * Opens a ply file for reading (fails if file is not a ply file) * * error_cb: error callback function * name: file name * * Returns 1 if successful, 0 otherwise * ---------------------------------------------------------------------- */ p_ply ply_open(const char *name, p_ply_error_cb error_cb); /* ---------------------------------------------------------------------- * Reads and parses the header of a ply file returned by ply_open * * ply: handle returned by ply_open * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_read_header(p_ply ply); /* ---------------------------------------------------------------------- * Property reading callback prototype * * argument: parameters for property being processed when callback is called * * Returns 1 if should continue processing file, 0 if should abort. * ---------------------------------------------------------------------- */ typedef int (*p_ply_read_cb)(p_ply_argument argument); /* ---------------------------------------------------------------------- * Sets up callbacks for property reading after header was parsed * * ply: handle returned by ply_open * element_name: element where property is * property_name: property to associate element with * read_cb: function to be called for each property value * pdata/idata: user data that will be passed to callback * * Returns 0 if no element or no property in element, returns the * number of element instances otherwise. * ---------------------------------------------------------------------- */ long 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); /* ---------------------------------------------------------------------- * Returns information about the element originating a callback * * argument: handle to argument * element: receives a the element handle (if non-null) * instance_index: receives the index of the current element instance * (if non-null) * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_get_argument_element(p_ply_argument argument, p_ply_element *element, long *instance_index); /* ---------------------------------------------------------------------- * Returns information about the property originating a callback * * argument: handle to argument * property: receives the property handle (if non-null) * length: receives the number of values in this property (if non-null) * value_index: receives the index of current property value (if non-null) * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_get_argument_property(p_ply_argument argument, p_ply_property *property, long *length, long *value_index); /* ---------------------------------------------------------------------- * Returns user data associated with callback * * pdata: receives a copy of user custom data pointer (if non-null) * idata: receives a copy of user custom data integer (if non-null) * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_get_argument_user_data(p_ply_argument argument, void **pdata, long *idata); /* ---------------------------------------------------------------------- * Returns the value associated with a callback * * argument: handle to argument * * Returns the current data item * ---------------------------------------------------------------------- */ double ply_get_argument_value(p_ply_argument argument); /* ---------------------------------------------------------------------- * Reads all elements and properties calling the callbacks defined with * calls to ply_set_read_cb * * ply: handle returned by ply_open * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_read(p_ply ply); /* ---------------------------------------------------------------------- * Iterates over all elements by returning the next element. * Call with NULL to return handle to first element. * * ply: handle returned by ply_open * last: handle of last element returned (NULL for first element) * * Returns element if successfull or NULL if no more elements * ---------------------------------------------------------------------- */ p_ply_element ply_get_next_element(p_ply ply, p_ply_element last); /* ---------------------------------------------------------------------- * Iterates over all comments by returning the next comment. * Call with NULL to return pointer to first comment. * * ply: handle returned by ply_open * last: pointer to last comment returned (NULL for first comment) * * Returns comment if successfull or NULL if no more comments * ---------------------------------------------------------------------- */ const char *ply_get_next_comment(p_ply ply, const char *last); /* ---------------------------------------------------------------------- * Iterates over all obj_infos by returning the next obj_info. * Call with NULL to return pointer to first obj_info. * * ply: handle returned by ply_open * last: pointer to last obj_info returned (NULL for first obj_info) * * Returns obj_info if successfull or NULL if no more obj_infos * ---------------------------------------------------------------------- */ const char *ply_get_next_obj_info(p_ply ply, const char *last); /* ---------------------------------------------------------------------- * Returns information about an element * * element: element of interest * name: receives a pointer to internal copy of element name (if non-null) * ninstances: receives the number of instances of this element (if non-null) * * Returns 1 if successfull or 0 otherwise * ---------------------------------------------------------------------- */ int ply_get_element_info(p_ply_element element, const char** name, long *ninstances); /* ---------------------------------------------------------------------- * Iterates over all properties by returning the next property. * Call with NULL to return handle to first property. * * element: handle of element with the properties of interest * last: handle of last property returned (NULL for first property) * * Returns element if successfull or NULL if no more properties * ---------------------------------------------------------------------- */ p_ply_property ply_get_next_property(p_ply_element element, p_ply_property last); /* ---------------------------------------------------------------------- * Returns information about a property * * property: handle to property of interest * name: receives a pointer to internal copy of property name (if non-null) * type: receives the property type (if non-null) * length_type: for list properties, receives the scalar type of * the length field (if non-null) * value_type: for list properties, receives the scalar type of the value * fields (if non-null) * * Returns 1 if successfull or 0 otherwise * ---------------------------------------------------------------------- */ int 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); /* ---------------------------------------------------------------------- * Creates new ply file * * name: file name * storage_mode: file format mode * * Returns handle to ply file if successfull, NULL otherwise * ---------------------------------------------------------------------- */ p_ply ply_create(const char *name, e_ply_storage_mode storage_mode, p_ply_error_cb error_cb); /* ---------------------------------------------------------------------- * Adds a new element to the ply file created by ply_create * * ply: handle returned by ply_create * name: name of new element * ninstances: number of element of this time in file * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_add_element(p_ply ply, const char *name, long ninstances); /* ---------------------------------------------------------------------- * Adds a new property to the last element added by ply_add_element * * ply: handle returned by ply_create * name: name of new property * type: property type * length_type: scalar type of length field of a list property * value_type: scalar type of value fields of a list property * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_add_property(p_ply ply, const char *name, e_ply_type type, e_ply_type length_type, e_ply_type value_type); /* ---------------------------------------------------------------------- * Adds a new list property to the last element added by ply_add_element * * ply: handle returned by ply_create * name: name of new property * length_type: scalar type of length field of a list property * value_type: scalar type of value fields of a list property * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_add_list_property(p_ply ply, const char *name, e_ply_type length_type, e_ply_type value_type); /* ---------------------------------------------------------------------- * Adds a new property to the last element added by ply_add_element * * ply: handle returned by ply_create * name: name of new property * type: property type * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type); /* ---------------------------------------------------------------------- * Adds a new comment item * * ply: handle returned by ply_create * comment: pointer to string with comment text * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_add_comment(p_ply ply, const char *comment); /* ---------------------------------------------------------------------- * Adds a new obj_info item * * ply: handle returned by ply_create * comment: pointer to string with obj_info data * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_add_obj_info(p_ply ply, const char *obj_info); /* ---------------------------------------------------------------------- * Writes the ply file header after all element and properties have been * defined by calls to ply_add_element and ply_add_property * * ply: handle returned by ply_create * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_write_header(p_ply ply); /* ---------------------------------------------------------------------- * Writes one property value, in the order they should be written to the * file. For each element type, write all elements of that type in order. * For each element, write all its properties in order. For scalar * properties, just write the value. For list properties, write the length * and then each of the values. * * ply: handle returned by ply_create * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_write(p_ply ply, double value); /* ---------------------------------------------------------------------- * Closes a ply file handle. Releases all memory used by handle * * ply: handle to be closed. * * Returns 1 if successfull, 0 otherwise * ---------------------------------------------------------------------- */ int ply_close(p_ply ply); #ifdef __cplusplus } #endif #endif /* RPLY_H */ /* ---------------------------------------------------------------------- * Copyright (C) 2003-2005 Diego Nehab. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ---------------------------------------------------------------------- */
file name: rply.c
/* ---------------------------------------------------------------------- * RPly library, read/write PLY files * Diego Nehab, Princeton University * http://www.cs.princeton.edu/~diego/professional/rply * * This library is distributed under the MIT License. See notice * at the end of this file. * ---------------------------------------------------------------------- */ #include <stdio.h> #include <ctype.h> #include <assert.h> #include <string.h> #include <limits.h> #include <float.h> #include <stdarg.h> #include <stdlib.h> #include <stddef.h> #include "rply.h" /* ---------------------------------------------------------------------- * Constants * ---------------------------------------------------------------------- */ #define WORDSIZE 256 #define LINESIZE 1024 #define BUFFERSIZE (8*1024) typedef enum e_ply_io_mode_ { PLY_READ, PLY_WRITE } e_ply_io_mode; static const char *const ply_storage_mode_list[] = { "binary_big_endian", "binary_little_endian", "ascii", NULL }; /* order matches e_ply_storage_mode enum */ static const char *const ply_type_list[] = { "int8", "uint8", "int16", "uint16", "int32", "uint32", "float32", "float64", "char", "uchar", "short", "ushort", "int", "uint", "float", "double", "list", NULL }; /* order matches e_ply_type enum */ /* ---------------------------------------------------------------------- * Property reading callback argument * * element: name of element being processed * property: name of property being processed * nelements: number of elements of this kind in file * instance_index: index current element of this kind being processed * length: number of values in current list (or 1 for scalars) * value_index: index of current value int this list (or 0 for scalars) * value: value of property * pdata/idata: user data defined with ply_set_cb * * Returns handle to ply file if succesful, NULL otherwise. * ---------------------------------------------------------------------- */ typedef struct t_ply_argument_ { p_ply_element element; long instance_index; p_ply_property property; long length, value_index; double value; void *pdata; long idata; } t_ply_argument; /* ---------------------------------------------------------------------- * Property information * * name: name of this property * type: type of this property (list or type of scalar value) * length_type, value_type: type of list property count and values * read_cb: function to be called when this property is called * * Returns 1 if should continue processing file, 0 if should abort. * ---------------------------------------------------------------------- */ typedef struct t_ply_property_ { char name[WORDSIZE]; e_ply_type type, value_type, length_type; p_ply_read_cb read_cb; void *pdata; long idata; } t_ply_property; /* ---------------------------------------------------------------------- * Element information * * name: name of this property * ninstances: number of elements of this type in file * property: property descriptions for this element * nproperty: number of properties in this element * * Returns 1 if should continue processing file, 0 if should abort. * ---------------------------------------------------------------------- */ typedef struct t_ply_element_ { char name[WORDSIZE]; long ninstances; p_ply_property property; long nproperties; } t_ply_element; /* ---------------------------------------------------------------------- * Input/output driver * * Depending on file mode, different functions are used to read/write * property fields. The drivers make it transparent to read/write in ascii, * big endian or little endian cases. * ---------------------------------------------------------------------- */ typedef int (*p_ply_ihandler)(p_ply ply, double *value); typedef int (*p_ply_ichunk)(p_ply ply, void *anydata, size_t size); typedef struct t_ply_idriver_ { p_ply_ihandler ihandler[16]; p_ply_ichunk ichunk; const char *name; } t_ply_idriver; typedef t_ply_idriver *p_ply_idriver; typedef int (*p_ply_ohandler)(p_ply ply, double value); typedef int (*p_ply_ochunk)(p_ply ply, void *anydata, size_t size); typedef struct t_ply_odriver_ { p_ply_ohandler ohandler[16]; p_ply_ochunk ochunk; const char *name; } t_ply_odriver; typedef t_ply_odriver *p_ply_odriver; /* ---------------------------------------------------------------------- * Ply file handle. * * io_mode: read or write (from e_ply_io_mode) * storage_mode: mode of file associated with handle (from e_ply_storage_mode) * element: elements description for this file * nelement: number of different elements in file * comment: comments for this file * ncomments: number of comments in file * obj_info: obj_info items for this file * nobj_infos: number of obj_info items in file * fp: file pointer associated with ply file * c: last character read from ply file * buffer: last word/chunck of data read from ply file * buffer_first, buffer_last: interval of untouched good data in buffer * buffer_token: start of parsed token (line or word) in buffer * idriver, odriver: input driver used to get property fields from file * argument: storage space for callback arguments * welement, wproperty: element/property type being written * winstance_index: index of instance of current element being written * wvalue_index: index of list property value being written * wlength: number of values in list property being written * error_cb: callback for error messages * ---------------------------------------------------------------------- */ typedef struct t_ply_ { e_ply_io_mode io_mode; e_ply_storage_mode storage_mode; p_ply_element element; long nelements; char *comment; long ncomments; char *obj_info; long nobj_infos; FILE *fp; int c; char buffer[BUFFERSIZE]; size_t buffer_first, buffer_token, buffer_last; p_ply_idriver idriver; p_ply_odriver odriver; t_ply_argument argument; long welement, wproperty; long winstance_index, wvalue_index, wlength; p_ply_error_cb error_cb; } t_ply; /* ---------------------------------------------------------------------- * I/O functions and drivers * ---------------------------------------------------------------------- */ static t_ply_idriver ply_idriver_ascii; static t_ply_idriver ply_idriver_binary; static t_ply_idriver ply_idriver_binary_reverse; static t_ply_odriver ply_odriver_ascii; static t_ply_odriver ply_odriver_binary; static t_ply_odriver ply_odriver_binary_reverse; static int ply_read_word(p_ply ply); static int ply_check_word(p_ply ply); static int ply_read_line(p_ply ply); static int ply_check_line(p_ply ply); static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size); static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size); static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size); static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size); static void ply_reverse(void *anydata, size_t size); /* ---------------------------------------------------------------------- * String functions * ---------------------------------------------------------------------- */ static int ply_find_string(const char *item, const char* const list[]); static p_ply_element ply_find_element(p_ply ply, const char *name); static p_ply_property ply_find_property(p_ply_element element, const char *name); /* ---------------------------------------------------------------------- * Header parsing * ---------------------------------------------------------------------- */ static int ply_read_header_format(p_ply ply); static int ply_read_header_comment(p_ply ply); static int ply_read_header_obj_info(p_ply ply); static int ply_read_header_property(p_ply ply); static int ply_read_header_element(p_ply ply); /* ---------------------------------------------------------------------- * Error handling * ---------------------------------------------------------------------- */ static void ply_error_cb(const char *message); static void ply_error(p_ply ply, const char *fmt, ...); /* ---------------------------------------------------------------------- * Memory allocation and initialization * ---------------------------------------------------------------------- */ static void ply_init(p_ply ply); static void ply_element_init(p_ply_element element); static void ply_property_init(p_ply_property property); static p_ply ply_alloc(void); static p_ply_element ply_grow_element(p_ply ply); static p_ply_property ply_grow_property(p_ply ply, p_ply_element element); static void *ply_grow_array(p_ply ply, void **pointer, long *nmemb, long size); /* ---------------------------------------------------------------------- * Special functions * ---------------------------------------------------------------------- */ static e_ply_storage_mode ply_arch_endian(void); static int ply_type_check(void); /* ---------------------------------------------------------------------- * Auxiliary read functions * ---------------------------------------------------------------------- */ static int ply_read_element(p_ply ply, p_ply_element element, p_ply_argument argument); static int ply_read_property(p_ply ply, p_ply_element element, p_ply_property property, p_ply_argument argument); static int ply_read_list_property(p_ply ply, p_ply_element element, p_ply_property property, p_ply_argument argument); static int ply_read_scalar_property(p_ply ply, p_ply_element element, p_ply_property property, p_ply_argument argument); /* ---------------------------------------------------------------------- * Buffer support functions * ---------------------------------------------------------------------- */ /* pointers to tokenized word and line in buffer */ #define BWORD(p) (p->buffer + p->buffer_token) #define BLINE(p) (p->buffer + p->buffer_token) /* pointer to start of untouched bytes in buffer */ #define BFIRST(p) (p->buffer + p->buffer_first) /* number of bytes untouched in buffer */ #define BSIZE(p) (p->buffer_last - p->buffer_first) /* consumes data from buffer */ #define BSKIP(p, s) (p->buffer_first += s) /* refills the buffer */ static int BREFILL(p_ply ply) { /* move untouched data to beginning of buffer */ size_t size = BSIZE(ply); memmove(ply->buffer, BFIRST(ply), size); ply->buffer_last = size; ply->buffer_first = ply->buffer_token = 0; /* fill remaining with new data */ size = fread(ply->buffer+size, 1, BUFFERSIZE-size-1, ply->fp); /* place sentinel so we can use str* functions with buffer */ ply->buffer[BUFFERSIZE-1] = '\0'; /* check if read failed */ if (size <= 0) return 0; /* increase size to account for new data */ ply->buffer_last += size; return 1; } /* ---------------------------------------------------------------------- * Exported functions * ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- * Read support functions * ---------------------------------------------------------------------- */ p_ply ply_open(const char *name, p_ply_error_cb error_cb) { char magic[5] = " "; FILE *fp = NULL; p_ply ply = NULL; if (error_cb == NULL) error_cb = ply_error_cb; if (!ply_type_check()) { error_cb("Incompatible type system"); return NULL; } assert(name); fp = fopen(name, "rb"); if (!fp) { error_cb("Unable to open file"); return NULL; } if (fread(magic, 1, 4, fp) < 4) { error_cb("Error reading from file"); fclose(fp); return NULL; } if (strcmp(magic, "ply\n")) { fclose(fp); error_cb("Not a PLY file. Expected magic number 'ply\\n'"); return NULL; } ply = ply_alloc(); if (!ply) { error_cb("Out of memory"); fclose(fp); return NULL; } ply->fp = fp; ply->io_mode = PLY_READ; ply->error_cb = error_cb; return ply; } int ply_read_header(p_ply ply) { assert(ply && ply->fp && ply->io_mode == PLY_READ); if (!ply_read_word(ply)) return 0; /* parse file format */ if (!ply_read_header_format(ply)) { ply_error(ply, "Invalid file format"); return 0; } /* parse elements, comments or obj_infos until the end of header */ while (strcmp(BWORD(ply), "end_header")) { if (!ply_read_header_comment(ply) && !ply_read_header_element(ply) && !ply_read_header_obj_info(ply)) { ply_error(ply, "Unexpected token '%s'", BWORD(ply)); return 0; } } return 1; } long 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) { p_ply_element element = NULL; p_ply_property property = NULL; assert(ply && element_name && property_name); element = ply_find_element(ply, element_name); if (!element) return 0; property = ply_find_property(element, property_name); if (!property) return 0; property->read_cb = read_cb; property->pdata = pdata; property->idata = idata; return (int) element->ninstances; } int ply_read(p_ply ply) { long i; p_ply_argument argument; assert(ply && ply->fp && ply->io_mode == PLY_READ); argument = &ply->argument; /* for each element type */ for (i = 0; i < ply->nelements; i++) { p_ply_element element = &ply->element[i]; argument->element = element; if (!ply_read_element(ply, element, argument)) return 0; } return 1; } /* ---------------------------------------------------------------------- * Write support functions * ---------------------------------------------------------------------- */ p_ply ply_create(const char *name, e_ply_storage_mode storage_mode, p_ply_error_cb error_cb) { FILE *fp = NULL; p_ply ply = NULL; if (error_cb == NULL) error_cb = ply_error_cb; if (!ply_type_check()) { error_cb("Incompatible type system"); return NULL; } assert(name && storage_mode <= PLY_DEFAULT); fp = fopen(name, "wb"); if (!fp) { error_cb("Unable to create file"); return NULL; } ply = ply_alloc(); if (!ply) { fclose(fp); error_cb("Out of memory"); return NULL; } ply->io_mode = PLY_WRITE; if (storage_mode == PLY_DEFAULT) storage_mode = ply_arch_endian(); if (storage_mode == PLY_ASCII) ply->odriver = &ply_odriver_ascii; else if (storage_mode == ply_arch_endian()) ply->odriver = &ply_odriver_binary; else ply->odriver = &ply_odriver_binary_reverse; ply->storage_mode = storage_mode; ply->fp = fp; ply->error_cb = error_cb; return ply; } int ply_add_element(p_ply ply, const char *name, long ninstances) { p_ply_element element = NULL; assert(ply && ply->fp && ply->io_mode == PLY_WRITE); assert(name && strlen(name) < WORDSIZE && ninstances >= 0); if (strlen(name) >= WORDSIZE || ninstances < 0) { ply_error(ply, "Invalid arguments"); return 0; } element = ply_grow_element(ply); if (!element) return 0; strcpy(element->name, name); element->ninstances = ninstances; return 1; } int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type) { p_ply_element element = NULL; p_ply_property property = NULL; assert(ply && ply->fp && ply->io_mode == PLY_WRITE); assert(name && strlen(name) < WORDSIZE); assert(type < PLY_LIST); if (strlen(name) >= WORDSIZE || type >= PLY_LIST) { ply_error(ply, "Invalid arguments"); return 0; } element = &ply->element[ply->nelements-1]; property = ply_grow_property(ply, element); if (!property) return 0; strcpy(property->name, name); property->type = type; return 1; } int ply_add_list_property(p_ply ply, const char *name, e_ply_type length_type, e_ply_type value_type) { p_ply_element element = NULL; p_ply_property property = NULL; assert(ply && ply->fp && ply->io_mode == PLY_WRITE); assert(name && strlen(name) < WORDSIZE); if (strlen(name) >= WORDSIZE) { ply_error(ply, "Invalid arguments"); return 0; } assert(length_type < PLY_LIST); assert(value_type < PLY_LIST); if (length_type >= PLY_LIST || value_type >= PLY_LIST) { ply_error(ply, "Invalid arguments"); return 0; } element = &ply->element[ply->nelements-1]; property = ply_grow_property(ply, element); if (!property) return 0; strcpy(property->name, name); property->type = PLY_LIST; property->length_type = length_type; property->value_type = value_type; return 1; } int ply_add_property(p_ply ply, const char *name, e_ply_type type, e_ply_type length_type, e_ply_type value_type) { if (type == PLY_LIST) return ply_add_list_property(ply, name, length_type, value_type); else return ply_add_scalar_property(ply, name, type); } int ply_add_comment(p_ply ply, const char *comment) { char *new_comment = NULL; assert(ply && comment && strlen(comment) < LINESIZE); if (!comment || strlen(comment) >= LINESIZE) { ply_error(ply, "Invalid arguments"); return 0; } new_comment = (char *) ply_grow_array(ply, (void **) &ply->comment, &ply->ncomments, LINESIZE); if (!new_comment) return 0; strcpy(new_comment, comment); return 1; } int ply_add_obj_info(p_ply ply, const char *obj_info) { char *new_obj_info = NULL; assert(ply && obj_info && strlen(obj_info) < LINESIZE); if (!obj_info || strlen(obj_info) >= LINESIZE) { ply_error(ply, "Invalid arguments"); return 0; } new_obj_info = (char *) ply_grow_array(ply, (void **) &ply->obj_info, &ply->nobj_infos, LINESIZE); if (!new_obj_info) return 0; strcpy(new_obj_info, obj_info); return 1; } int ply_write_header(p_ply ply) { long i, j; assert(ply && ply->fp && ply->io_mode == PLY_WRITE); assert(ply->element || ply->nelements == 0); assert(!ply->element || ply->nelements > 0); if (fprintf(ply->fp, "ply\nformat %s 1.0\n", ply_storage_mode_list[ply->storage_mode]) <= 0) goto error; for (i = 0; i < ply->ncomments; i++) if (fprintf(ply->fp, "comment %s\n", ply->comment + LINESIZE*i) <= 0) goto error; for (i = 0; i < ply->nobj_infos; i++) if (fprintf(ply->fp, "obj_info %s\n", ply->obj_info + LINESIZE*i) <= 0) goto error; for (i = 0; i < ply->nelements; i++) { p_ply_element element = &ply->element[i]; assert(element->property || element->nproperties == 0); assert(!element->property || element->nproperties > 0); if (fprintf(ply->fp, "element %s %ld\n", element->name, element->ninstances) <= 0) goto error; for (j = 0; j < element->nproperties; j++) { p_ply_property property = &element->property[j]; if (property->type == PLY_LIST) { if (fprintf(ply->fp, "property list %s %s %s\n", ply_type_list[property->length_type], ply_type_list[property->value_type], property->name) <= 0) goto error; } else { if (fprintf(ply->fp, "property %s %s\n", ply_type_list[property->type], property->name) <= 0) goto error; } } } return fprintf(ply->fp, "end_header\n") > 0; error: ply_error(ply, "Error writing to file"); return 0; } int ply_write(p_ply ply, double value) { p_ply_element element = NULL; p_ply_property property = NULL; int type = -1; int breakafter = 0; if (ply->welement > ply->nelements) return 0; element = &ply->element[ply->welement]; if (ply->wproperty > element->nproperties) return 0; property = &element->property[ply->wproperty]; if (property->type == PLY_LIST) { if (ply->wvalue_index == 0) { type = property->length_type; ply->wlength = (long) value; } else type = property->value_type; } else { type = property->type; ply->wlength = 0; } if (!ply->odriver->ohandler[type](ply, value)) { ply_error(ply, "Failed writing %s of %s %d (%s: %s)", property->name, element->name, ply->winstance_index, ply->odriver->name, ply_type_list[type]); return 0; } ply->wvalue_index++; if (ply->wvalue_index > ply->wlength) { ply->wvalue_index = 0; ply->wproperty++; } if (ply->wproperty >= element->nproperties) { ply->wproperty = 0; ply->winstance_index++; if (ply->storage_mode == PLY_ASCII) breakafter = 1; } if (ply->winstance_index >= element->ninstances) { ply->winstance_index = 0; ply->welement++; } return !breakafter || putc('\n', ply->fp) > 0; } int ply_close(p_ply ply) { long i; assert(ply && ply->fp); assert(ply->element || ply->nelements == 0); assert(!ply->element || ply->nelements > 0); /* write last chunk to file */ if (ply->io_mode == PLY_WRITE && fwrite(ply->buffer, 1, ply->buffer_last, ply->fp) < ply->buffer_last) { ply_error(ply, "Error closing up"); return 0; } fclose(ply->fp); /* free all memory used by handle */ if (ply->element) { for (i = 0; i < ply->nelements; i++) { p_ply_element element = &ply->element[i]; if (element->property) free(element->property); } free(ply->element); } if (ply->obj_info) free(ply->obj_info); if (ply->comment) free(ply->comment); free(ply); return 1; } /* ---------------------------------------------------------------------- * Query support functions * ---------------------------------------------------------------------- */ p_ply_element ply_get_next_element(p_ply ply, p_ply_element last) { assert(ply); if (!last) return ply->element; last++; if (last < ply->element + ply->nelements) return last; else return NULL; } int ply_get_element_info(p_ply_element element, const char** name, long *ninstances) { assert(element); if (name) *name = element->name; if (ninstances) *ninstances = (long) element->ninstances; return 1; } p_ply_property ply_get_next_property(p_ply_element element, p_ply_property last) { assert(element); if (!last) return element->property; last++; if (last < element->property + element->nproperties) return last; else return NULL; } int 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) { assert(property); if (name) *name = property->name; if (type) *type = property->type; if (length_type) *length_type = property->length_type; if (value_type) *value_type = property->value_type; return 1; } const char *ply_get_next_comment(p_ply ply, const char *last) { assert(ply); if (!last) return ply->comment; last += LINESIZE; if (last < ply->comment + LINESIZE*ply->ncomments) return last; else return NULL; } const char *ply_get_next_obj_info(p_ply ply, const char *last) { assert(ply); if (!last) return ply->obj_info; last += LINESIZE; if (last < ply->obj_info + LINESIZE*ply->nobj_infos) return last; else return NULL; } /* ---------------------------------------------------------------------- * Callback argument support functions * ---------------------------------------------------------------------- */ int ply_get_argument_element(p_ply_argument argument, p_ply_element *element, long *instance_index) { assert(argument); if (!argument) return 0; if (element) *element = argument->element; if (instance_index) *instance_index = argument->instance_index; return 1; } int ply_get_argument_property(p_ply_argument argument, p_ply_property *property, long *length, long *value_index) { assert(argument); if (!argument) return 0; if (property) *property = argument->property; if (length) *length = argument->length; if (value_index) *value_index = argument->value_index; return 1; } int ply_get_argument_user_data(p_ply_argument argument, void **pdata, long *idata) { assert(argument); if (!argument) return 0; if (pdata) *pdata = argument->pdata; if (idata) *idata = argument->idata; return 1; } double ply_get_argument_value(p_ply_argument argument) { assert(argument); if (!argument) return 0.0; return argument->value; } /* ---------------------------------------------------------------------- * Internal functions * ---------------------------------------------------------------------- */ static int ply_read_list_property(p_ply ply, p_ply_element element, p_ply_property property, p_ply_argument argument) { int l; p_ply_read_cb read_cb = property->read_cb; p_ply_ihandler *driver = ply->idriver->ihandler; /* get list length */ p_ply_ihandler handler = driver[property->length_type]; double length; if (!handler(ply, &length)) { ply_error(ply, "Error reading '%s' of '%s' number %d", property->name, element->name, argument->instance_index); return 0; } /* invoke callback to pass length in value field */ argument->length = (long) length; argument->value_index = -1; argument->value = length; if (read_cb && !read_cb(argument)) { ply_error(ply, "Aborted by user"); return 0; } /* read list values */ handler = driver[property->value_type]; /* for each value in list */ for (l = 0; l < (long) length; l++) { /* read value from file */ argument->value_index = l; if (!handler(ply, &argument->value)) { ply_error(ply, "Error reading value number %d of '%s' of " "'%s' number %d", l+1, property->name, element->name, argument->instance_index); return 0; } /* invoke callback to pass value */ if (read_cb && !read_cb(argument)) { ply_error(ply, "Aborted by user"); return 0; } } return 1; } static int ply_read_scalar_property(p_ply ply, p_ply_element element, p_ply_property property, p_ply_argument argument) { p_ply_read_cb read_cb = property->read_cb; p_ply_ihandler *driver = ply->idriver->ihandler; p_ply_ihandler handler = driver[property->type]; argument->length = 1; argument->value_index = 0; if (!handler(ply, &argument->value)) { ply_error(ply, "Error reading '%s' of '%s' number %d", property->name, element->name, argument->instance_index); return 0; } if (read_cb && !read_cb(argument)) { ply_error(ply, "Aborted by user"); return 0; } return 1; } static int ply_read_property(p_ply ply, p_ply_element element, p_ply_property property, p_ply_argument argument) { if (property->type == PLY_LIST) return ply_read_list_property(ply, element, property, argument); else return ply_read_scalar_property(ply, element, property, argument); } static int ply_read_element(p_ply ply, p_ply_element element, p_ply_argument argument) { long j, k; /* for each element of this type */ for (j = 0; j < element->ninstances; j++) { argument->instance_index = j; /* for each property */ for (k = 0; k < element->nproperties; k++) { p_ply_property property = &element->property[k]; argument->property = property; argument->pdata = property->pdata; argument->idata = property->idata; if (!ply_read_property(ply, element, property, argument)) return 0; } } return 1; } static int ply_find_string(const char *item, const char* const list[]) { int i; assert(item && list); for (i = 0; list[i]; i++) if (!strcmp(list[i], item)) return i; return -1; } static p_ply_element ply_find_element(p_ply ply, const char *name) { p_ply_element element; int i, nelements; assert(ply && name); element = ply->element; nelements = ply->nelements; assert(element || nelements == 0); assert(!element || nelements > 0); for (i = 0; i < nelements; i++) if (!strcmp(element[i].name, name)) return &element[i]; return NULL; } static p_ply_property ply_find_property(p_ply_element element, const char *name) { p_ply_property property; int i, nproperties; assert(element && name); property = element->property; nproperties = element->nproperties; assert(property || nproperties == 0); assert(!property || nproperties > 0); for (i = 0; i < nproperties; i++) if (!strcmp(property[i].name, name)) return &property[i]; return NULL; } static int ply_check_word(p_ply ply) { if (strlen(BLINE(ply)) >= WORDSIZE) { ply_error(ply, "Word too long"); return 0; } return 1; } static int ply_read_word(p_ply ply) { size_t t = 0; assert(ply && ply->fp && ply->io_mode == PLY_READ); /* skip leading blanks */ while (1) { t = strspn(BFIRST(ply), " \n\r\t"); /* check if all buffer was made of blanks */ if (t >= BSIZE(ply)) { if (!BREFILL(ply)) { ply_error(ply, "Unexpected end of file"); return 0; } } else break; } BSKIP(ply, t); /* look for a space after the current word */ t = strcspn(BFIRST(ply), " \n\r\t"); /* if we didn't reach the end of the buffer, we are done */ if (t < BSIZE(ply)) { ply->buffer_token = ply->buffer_first; BSKIP(ply, t); *BFIRST(ply) = '\0'; BSKIP(ply, 1); return ply_check_word(ply); } /* otherwise, try to refill buffer */ if (!BREFILL(ply)) { ply_error(ply, "Unexpected end of file"); return 0; } /* keep looking from where we left */ t += strcspn(BFIRST(ply) + t, " \n\r\t"); /* check if the token is too large for our buffer */ if (t >= BSIZE(ply)) { ply_error(ply, "Token too large"); return 0; } /* we are done */ ply->buffer_token = ply->buffer_first; BSKIP(ply, t); *BFIRST(ply) = '\0'; BSKIP(ply, 1); return ply_check_word(ply); } static int ply_check_line(p_ply ply) { if (strlen(BLINE(ply)) >= LINESIZE) { ply_error(ply, "Line too long"); return 0; } return 1; } static int ply_read_line(p_ply ply) { const char *end = NULL; assert(ply && ply->fp && ply->io_mode == PLY_READ); /* look for a end of line */ end = strchr(BFIRST(ply), '\n'); /* if we didn't reach the end of the buffer, we are done */ if (end) { ply->buffer_token = ply->buffer_first; BSKIP(ply, end - BFIRST(ply)); *BFIRST(ply) = '\0'; BSKIP(ply, 1); return ply_check_line(ply); } else { end = ply->buffer + BSIZE(ply); /* otherwise, try to refill buffer */ if (!BREFILL(ply)) { ply_error(ply, "Unexpected end of file"); return 0; } } /* keep looking from where we left */ end = strchr(end, '\n'); /* check if the token is too large for our buffer */ if (!end) { ply_error(ply, "Token too large"); return 0; } /* we are done */ ply->buffer_token = ply->buffer_first; BSKIP(ply, end - BFIRST(ply)); *BFIRST(ply) = '\0'; BSKIP(ply, 1); return ply_check_line(ply); } static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size) { char *buffer = (char *) anybuffer; size_t i = 0; assert(ply && ply->fp && ply->io_mode == PLY_READ); assert(ply->buffer_first <= ply->buffer_last); while (i < size) { if (ply->buffer_first < ply->buffer_last) { buffer[i] = ply->buffer[ply->buffer_first]; ply->buffer_first++; i++; } else { ply->buffer_first = 0; ply->buffer_last = fread(ply->buffer, 1, BUFFERSIZE, ply->fp); if (ply->buffer_last <= 0) return 0; } } return 1; } static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size) { char *buffer = (char *) anybuffer; size_t i = 0; assert(ply && ply->fp && ply->io_mode == PLY_WRITE); assert(ply->buffer_last <= BUFFERSIZE); while (i < size) { if (ply->buffer_last < BUFFERSIZE) { ply->buffer[ply->buffer_last] = buffer[i]; ply->buffer_last++; i++; } else { ply->buffer_last = 0; if (fwrite(ply->buffer, 1, BUFFERSIZE, ply->fp) < BUFFERSIZE) return 0; } } return 1; } static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size) { int ret = 0; ply_reverse(anybuffer, size); ret = ply_write_chunk(ply, anybuffer, size); ply_reverse(anybuffer, size); return ret; } static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size) { if (!ply_read_chunk(ply, anybuffer, size)) return 0; ply_reverse(anybuffer, size); return 1; } static void ply_reverse(void *anydata, size_t size) { char *data = (char *) anydata; char temp; size_t i; for (i = 0; i < size/2; i++) { temp = data[i]; data[i] = data[size-i-1]; data[size-i-1] = temp; } } static void ply_init(p_ply ply) { ply->c = ' '; ply->element = NULL; ply->nelements = 0; ply->comment = NULL; ply->ncomments = 0; ply->obj_info = NULL; ply->nobj_infos = 0; ply->idriver = NULL; ply->odriver = NULL; ply->buffer[0] = '\0'; ply->buffer_first = ply->buffer_last = ply->buffer_token = 0; ply->welement = 0; ply->wproperty = 0; ply->winstance_index = 0; ply->wlength = 0; ply->wvalue_index = 0; } static void ply_element_init(p_ply_element element) { element->name[0] = '\0'; element->ninstances = 0; element->property = NULL; element->nproperties = 0; } static void ply_property_init(p_ply_property property) { property->name[0] = '\0'; property->type = -1; property->length_type = -1; property->value_type = -1; property->read_cb = (p_ply_read_cb) NULL; property->pdata = NULL; property->idata = 0; } static p_ply ply_alloc(void) { p_ply ply = (p_ply) malloc(sizeof(t_ply)); if (!ply) return NULL; ply_init(ply); return ply; } static void *ply_grow_array(p_ply ply, void **pointer, long *nmemb, long size) { void *temp = *pointer; long count = *nmemb + 1; if (!temp) temp = malloc(count*size); else temp = realloc(temp, count*size); if (!temp) { ply_error(ply, "Out of memory"); return NULL; } *pointer = temp; *nmemb = count; return (char *) temp + (count-1) * size; } static p_ply_element ply_grow_element(p_ply ply) { p_ply_element element = NULL; assert(ply); assert(ply->element || ply->nelements == 0); assert(!ply->element || ply->nelements > 0); element = (p_ply_element) ply_grow_array(ply, (void **) &ply->element, &ply->nelements, sizeof(t_ply_element)); if (!element) return NULL; ply_element_init(element); return element; } static p_ply_property ply_grow_property(p_ply ply, p_ply_element element) { p_ply_property property = NULL; assert(ply); assert(element); assert(element->property || element->nproperties == 0); assert(!element->property || element->nproperties > 0); property = (p_ply_property) ply_grow_array(ply, (void **) &element->property, &element->nproperties, sizeof(t_ply_property)); if (!property) return NULL; ply_property_init(property); return property; } static int ply_read_header_format(p_ply ply) { assert(ply && ply->fp && ply->io_mode == PLY_READ); if (strcmp(BWORD(ply), "format")) return 0; if (!ply_read_word(ply)) return 0; ply->storage_mode = ply_find_string(BWORD(ply), ply_storage_mode_list); if (ply->storage_mode == (e_ply_storage_mode) (-1)) return 0; if (ply->storage_mode == PLY_ASCII) ply->idriver = &ply_idriver_ascii; else if (ply->storage_mode == ply_arch_endian()) ply->idriver = &ply_idriver_binary; else ply->idriver = &ply_idriver_binary_reverse; if (!ply_read_word(ply)) return 0; if (strcmp(BWORD(ply), "1.0")) return 0; if (!ply_read_word(ply)) return 0; return 1; } static int ply_read_header_comment(p_ply ply) { assert(ply && ply->fp && ply->io_mode == PLY_READ); if (strcmp(BWORD(ply), "comment")) return 0; if (!ply_read_line(ply)) return 0; if (!ply_add_comment(ply, BLINE(ply))) return 0; if (!ply_read_word(ply)) return 0; return 1; } static int ply_read_header_obj_info(p_ply ply) { assert(ply && ply->fp && ply->io_mode == PLY_READ); if (strcmp(BWORD(ply), "obj_info")) return 0; if (!ply_read_line(ply)) return 0; if (!ply_add_obj_info(ply, BLINE(ply))) return 0; if (!ply_read_word(ply)) return 0; return 1; } static int ply_read_header_property(p_ply ply) { p_ply_element element = NULL; p_ply_property property = NULL; /* make sure it is a property */ if (strcmp(BWORD(ply), "property")) return 0; element = &ply->element[ply->nelements-1]; property = ply_grow_property(ply, element); if (!property) return 0; /* get property type */ if (!ply_read_word(ply)) return 0; property->type = ply_find_string(BWORD(ply), ply_type_list); if (property->type == (e_ply_type) (-1)) return 0; if (property->type == PLY_LIST) { /* if it's a list, we need the base types */ if (!ply_read_word(ply)) return 0; property->length_type = ply_find_string(BWORD(ply), ply_type_list); if (property->length_type == (e_ply_type) (-1)) return 0; if (!ply_read_word(ply)) return 0; property->value_type = ply_find_string(BWORD(ply), ply_type_list); if (property->value_type == (e_ply_type) (-1)) return 0; } /* get property name */ if (!ply_read_word(ply)) return 0; strcpy(property->name, BWORD(ply)); if (!ply_read_word(ply)) return 0; return 1; } static int ply_read_header_element(p_ply ply) { p_ply_element element = NULL; long dummy; assert(ply && ply->fp && ply->io_mode == PLY_READ); if (strcmp(BWORD(ply), "element")) return 0; /* allocate room for new element */ element = ply_grow_element(ply); if (!element) return 0; /* get element name */ if (!ply_read_word(ply)) return 0; strcpy(element->name, BWORD(ply)); /* get number of elements of this type */ if (!ply_read_word(ply)) return 0; if (sscanf(BWORD(ply), "%ld", &dummy) != 1) { ply_error(ply, "Expected number got '%s'", BWORD(ply)); return 0; } element->ninstances = dummy; /* get all properties for this element */ if (!ply_read_word(ply)) return 0; while (ply_read_header_property(ply) || ply_read_header_comment(ply) || ply_read_header_obj_info(ply)) /* do nothing */; return 1; } static void ply_error_cb(const char *message) { fprintf(stderr, "RPly: %s\n", message); } static void ply_error(p_ply ply, const char *fmt, ...) { char buffer[1024]; va_list ap; va_start(ap, fmt); vsprintf(buffer, fmt, ap); va_end(ap); ply->error_cb(buffer); } static e_ply_storage_mode ply_arch_endian(void) { unsigned long i = 1; unsigned char *s = (unsigned char *) &i; if (*s == 1) return PLY_LITTLE_ENDIAN; else return PLY_BIG_ENDIAN; } static int ply_type_check(void) { assert(sizeof(char) == 1); assert(sizeof(unsigned char) == 1); assert(sizeof(short) == 2); assert(sizeof(unsigned short) == 2); assert(sizeof(long) == 4); assert(sizeof(unsigned long) == 4); assert(sizeof(float) == 4); assert(sizeof(double) == 8); if (sizeof(char) != 1) return 0; if (sizeof(unsigned char) != 1) return 0; if (sizeof(short) != 2) return 0; if (sizeof(unsigned short) != 2) return 0; if (sizeof(long) != 4) return 0; if (sizeof(unsigned long) != 4) return 0; if (sizeof(float) != 4) return 0; if (sizeof(double) != 8) return 0; return 1; } /* ---------------------------------------------------------------------- * Output handlers * ---------------------------------------------------------------------- */ static int oascii_int8(p_ply ply, double value) { if (value > CHAR_MAX || value < CHAR_MIN) return 0; return fprintf(ply->fp, "%d ", (char) value) > 0; } static int oascii_uint8(p_ply ply, double value) { if (value > UCHAR_MAX || value < 0) return 0; return fprintf(ply->fp, "%d ", (unsigned char) value) > 0; } static int oascii_int16(p_ply ply, double value) { if (value > SHRT_MAX || value < SHRT_MIN) return 0; return fprintf(ply->fp, "%d ", (short) value) > 0; } static int oascii_uint16(p_ply ply, double value) { if (value > USHRT_MAX || value < 0) return 0; return fprintf(ply->fp, "%d ", (unsigned short) value) > 0; } static int oascii_int32(p_ply ply, double value) { if (value > LONG_MAX || value < LONG_MIN) return 0; return fprintf(ply->fp, "%d ", (int) value) > 0; } static int oascii_uint32(p_ply ply, double value) { if (value > ULONG_MAX || value < 0) return 0; return fprintf(ply->fp, "%d ", (unsigned int) value) > 0; } static int oascii_float32(p_ply ply, double value) { if (value < -FLT_MAX || value > FLT_MAX) return 0; return fprintf(ply->fp, "%g ", (float) value) > 0; } static int oascii_float64(p_ply ply, double value) { if (value < -DBL_MAX || value > DBL_MAX) return 0; return fprintf(ply->fp, "%g ", value) > 0; } static int obinary_int8(p_ply ply, double value) { char int8 = (char) value; if (value > CHAR_MAX || value < CHAR_MIN) return 0; return ply->odriver->ochunk(ply, &int8, sizeof(int8)); } static int obinary_uint8(p_ply ply, double value) { unsigned char uint8 = (unsigned char) value; if (value > UCHAR_MAX || value < 0) return 0; return ply->odriver->ochunk(ply, &uint8, sizeof(uint8)); } static int obinary_int16(p_ply ply, double value) { short int16 = (short) value; if (value > SHRT_MAX || value < SHRT_MIN) return 0; return ply->odriver->ochunk(ply, &int16, sizeof(int16)); } static int obinary_uint16(p_ply ply, double value) { unsigned short uint16 = (unsigned short) value; if (value > USHRT_MAX || value < 0) return 0; return ply->odriver->ochunk(ply, &uint16, sizeof(uint16)); } static int obinary_int32(p_ply ply, double value) { long int32 = (long) value; if (value > LONG_MAX || value < LONG_MIN) return 0; return ply->odriver->ochunk(ply, &int32, sizeof(int32)); } static int obinary_uint32(p_ply ply, double value) { unsigned long uint32 = (unsigned long) value; if (value > ULONG_MAX || value < 0) return 0; return ply->odriver->ochunk(ply, &uint32, sizeof(uint32)); } static int obinary_float32(p_ply ply, double value) { float float32 = (float) value; if (value > FLT_MAX || value < -FLT_MAX) return 0; return ply->odriver->ochunk(ply, &float32, sizeof(float32)); } static int obinary_float64(p_ply ply, double value) { return ply->odriver->ochunk(ply, &value, sizeof(value)); } /* ---------------------------------------------------------------------- * Input handlers * ---------------------------------------------------------------------- */ static int iascii_int8(p_ply ply, double *value) { char *end; if (!ply_read_word(ply)) return 0; *value = strtol(BWORD(ply), &end, 10); if (*end || *value > CHAR_MAX || *value < CHAR_MIN) return 0; return 1; } static int iascii_uint8(p_ply ply, double *value) { char *end; if (!ply_read_word(ply)) return 0; *value = strtol(BWORD(ply), &end, 10); if (*end || *value > UCHAR_MAX || *value < 0) return 0; return 1; } static int iascii_int16(p_ply ply, double *value) { char *end; if (!ply_read_word(ply)) return 0; *value = strtol(BWORD(ply), &end, 10); if (*end || *value > SHRT_MAX || *value < SHRT_MIN) return 0; return 1; } static int iascii_uint16(p_ply ply, double *value) { char *end; if (!ply_read_word(ply)) return 0; *value = strtol(BWORD(ply), &end, 10); if (*end || *value > USHRT_MAX || *value < 0) return 0; return 1; } static int iascii_int32(p_ply ply, double *value) { char *end; if (!ply_read_word(ply)) return 0; *value = strtol(BWORD(ply), &end, 10); if (*end || *value > LONG_MAX || *value < LONG_MIN) return 0; return 1; } static int iascii_uint32(p_ply ply, double *value) { char *end; if (!ply_read_word(ply)) return 0; *value = strtol(BWORD(ply), &end, 10); if (*end || *value < 0) return 0; return 1; } static int iascii_float32(p_ply ply, double *value) { char *end; if (!ply_read_word(ply)) return 0; *value = strtod(BWORD(ply), &end); if (*end || *value < -FLT_MAX || *value > FLT_MAX) return 0; return 1; } static int iascii_float64(p_ply ply, double *value) { char *end; if (!ply_read_word(ply)) return 0; *value = strtod(BWORD(ply), &end); if (*end || *value < -DBL_MAX || *value > DBL_MAX) return 0; return 1; } static int ibinary_int8(p_ply ply, double *value) { char int8; if (!ply->idriver->ichunk(ply, &int8, 1)) return 0; *value = int8; return 1; } static int ibinary_uint8(p_ply ply, double *value) { unsigned char uint8; if (!ply->idriver->ichunk(ply, &uint8, 1)) return 0; *value = uint8; return 1; } static int ibinary_int16(p_ply ply, double *value) { short int16; if (!ply->idriver->ichunk(ply, &int16, sizeof(int16))) return 0; *value = int16; return 1; } static int ibinary_uint16(p_ply ply, double *value) { unsigned short uint16; if (!ply->idriver->ichunk(ply, &uint16, sizeof(uint16))) return 0; *value = uint16; return 1; } static int ibinary_int32(p_ply ply, double *value) { long int32; if (!ply->idriver->ichunk(ply, &int32, sizeof(int32))) return 0; *value = int32; return 1; } static int ibinary_uint32(p_ply ply, double *value) { unsigned long uint32; if (!ply->idriver->ichunk(ply, &uint32, sizeof(uint32))) return 0; *value = uint32; return 1; } static int ibinary_float32(p_ply ply, double *value) { float float32; if (!ply->idriver->ichunk(ply, &float32, sizeof(float32))) return 0; *value = float32; ply_reverse(&float32, sizeof(float32)); return 1; } static int ibinary_float64(p_ply ply, double *value) { return ply->idriver->ichunk(ply, value, sizeof(double)); } /* ---------------------------------------------------------------------- * Constants * ---------------------------------------------------------------------- */ static t_ply_idriver ply_idriver_ascii = { { iascii_int8, iascii_uint8, iascii_int16, iascii_uint16, iascii_int32, iascii_uint32, iascii_float32, iascii_float64, iascii_int8, iascii_uint8, iascii_int16, iascii_uint16, iascii_int32, iascii_uint32, iascii_float32, iascii_float64 }, /* order matches e_ply_type enum */ NULL, "ascii input" }; static t_ply_idriver ply_idriver_binary = { { ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16, ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64, ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16, ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64 }, /* order matches e_ply_type enum */ ply_read_chunk, "binary input" }; static t_ply_idriver ply_idriver_binary_reverse = { { ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16, ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64, ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16, ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64 }, /* order matches e_ply_type enum */ ply_read_chunk_reverse, "reverse binary input" }; static t_ply_odriver ply_odriver_ascii = { { oascii_int8, oascii_uint8, oascii_int16, oascii_uint16, oascii_int32, oascii_uint32, oascii_float32, oascii_float64, oascii_int8, oascii_uint8, oascii_int16, oascii_uint16, oascii_int32, oascii_uint32, oascii_float32, oascii_float64 }, /* order matches e_ply_type enum */ NULL, "ascii output" }; static t_ply_odriver ply_odriver_binary = { { obinary_int8, obinary_uint8, obinary_int16, obinary_uint16, obinary_int32, obinary_uint32, obinary_float32, obinary_float64, obinary_int8, obinary_uint8, obinary_int16, obinary_uint16, obinary_int32, obinary_uint32, obinary_float32, obinary_float64 }, /* order matches e_ply_type enum */ ply_write_chunk, "binary output" }; static t_ply_odriver ply_odriver_binary_reverse = { { obinary_int8, obinary_uint8, obinary_int16, obinary_uint16, obinary_int32, obinary_uint32, obinary_float32, obinary_float64, obinary_int8, obinary_uint8, obinary_int16, obinary_uint16, obinary_int32, obinary_uint32, obinary_float32, obinary_float64 }, /* order matches e_ply_type enum */ ply_write_chunk_reverse, "reverse binary output" }; /* ---------------------------------------------------------------------- * Copyright (C) 2003 Diego Nehab. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ---------------------------------------------------------------------- */
file name: sconvert.c
#include <stdio.h> #include "rply.h" #include "convert.h" #include <windows.h> #include <winbase.h> static int callback(p_ply_argument argument) { void *pdata; /* just pass the value from the input file to the output file */ ply_get_argument_user_data(argument, &pdata, NULL); ply_write((p_ply) pdata, ply_get_argument_value(argument)); return 1; } static int setup_callbacks(p_ply iply, p_ply oply) { p_ply_element element = NULL; /* iterate over all elements in input file */ while ((element = ply_get_next_element(iply, element))) { p_ply_property property = NULL; long nelems = 0; const char *element_name; ply_get_element_info(element, &element_name, &nelems); /* add this element to output file */ if (!ply_add_element(oply, element_name, nelems)) return 0; /* iterate over all properties of current element */ while ((property = ply_get_next_property(element, property))) { const char *property_name; e_ply_type type, length_type, value_type; ply_get_property_info(property, &property_name, &type, &length_type, &value_type); /* setup input callback for this property */ if (!ply_set_read_cb(iply, element_name, property_name, callback, oply, 0)) return 0; /* add this property to output file */ if (!ply_add_property(oply, property_name, type, length_type, value_type)) return 0; } } return 1; } int convert(char* fileName) { const char *value; char buf[32]; p_ply iply, oply; GetTempFileName(".", "whatever", 0, buf); if (MoveFileEx(fileName, buf, MOVEFILE_REPLACE_EXISTING)==0) { printf("rename fails with error code %d\n", GetLastError()); return 2; } iply = ply_open(buf, NULL); if (!iply) return 1; if (!ply_read_header(iply)) return 1; oply = ply_create(fileName, PLY_LITTLE_ENDIAN, NULL); if (!oply) return 1; if (!setup_callbacks(iply, oply)) return 1; //pass comments and obj_infos from input to output value = NULL; while ((value = ply_get_next_comment(iply, value))) if (!ply_add_comment(oply, value)) return 1; value = NULL; while ((value = ply_get_next_obj_info(iply, value))) if (!ply_add_obj_info(oply, value)) return 1;; // write output header if (!ply_write_header(oply)) return 1; // read input file generating callbacks that pass data to output file / if (!ply_read(iply)) return 1; // close up, we are done if (!ply_close(iply)) return 1; if (!ply_close(oply)) return 1; return 0; }
file name: main.c
#include <iostream> #include "rply.h" using namespace std; #include "sconvert.c" //#include "rply.h" typedef float FVector[3]; typedef unsigned char UCVector[3]; //extern int convert(char* fileName); struct PlyFace { unsigned char number; int* indexList; UCVector color; }; struct PlyModel { int vNumber; int fNumber; FVector* coord; UCVector* color; FVector* normal; PlyFace* faceList; bool analysis(char* buf, PlyModel& ply); int readFile(char* fileName); }; bool PlyModel::analysis(char* buf, PlyModel& ply) { char* token; token=strtok(buf, " \n"); while (token!=NULL) { if (strcmp(token, "vertex")==0) { token=strtok(NULL, " \n"); sscanf(token, "%d", &ply.vNumber); break; } if (strcmp(token, "face")==0) { token=strtok(NULL, " \n"); sscanf(token, "%d", &ply.fNumber); break; } if (strcmp(token, "end_header" )==0) { return true; } token=strtok(NULL, " \n"); } return false; } int PlyModel::readFile(char* fileName) { FILE* file; char buffer[256]; int i; PlyModel ply; convert(fileName); file=fopen(fileName, "r+b"); while (true) { fgets(buffer, 256, file); //i=fread(buffer, 1, 256, file); if (analysis(buffer, ply)) { break; } } ply.coord=new FVector[ply.vNumber]; ply.color=new UCVector[ply.vNumber]; ply.normal=new FVector[ply.vNumber]; ply.faceList=new PlyFace[ply.fNumber]; for (i=0; i<ply.vNumber; i++) { fread(ply.coord[i], sizeof(float), 3, file); fread(ply.color[i], sizeof(unsigned char), 3, file); fread(ply.normal[i], sizeof(float), 3, file); } for (i=0; i<ply.fNumber; i++) { fread(&ply.faceList[i].number, sizeof(unsigned char), 1, file); ply.faceList[i].indexList=new int[ply.faceList[i].number]; fread(ply.faceList[i].indexList, sizeof(int), ply.faceList[i].number, file); fread(ply.faceList[i].color, sizeof(unsigned char), 3, file); } fclose(file); return 0; } int main() { PlyModel ply; ply.readFile("g0.ply"); return 0; }
///////////////////////////////
The input g0.ply file is a typical ply format data file. The header is like following:
ply format binary_big_endian 1.0 element vertex 3259 property float x property float y property float z property uchar red property uchar green property uchar blue property float nx property float ny property float nz element face 4128 property list uchar int vertex_indices property uchar red property uchar green property uchar blue end_header
And from this header you are absolutely unable to realize what it makes difference from little-endian because
these are characters. Of course only as blind as I who cannot read "big-endian" in this header. However, I
just refused to admit it.