ply reading

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.

    ¡¡

    B.The problem

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.

¡¡

C.The idea of program
¡¡

This is a just personal practicing and testing for my project.

¡¡

¡¡

    D.The major functions

    ¡¡

    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.

    ¡¡

    ¡¡

    ¡¡