/*
 * LWSDK Header File
 * Copyright 1999, NewTek, Inc.
 *
 * LWIO.H -- LightWave Save and Load State
 *
 * This header contains the definition of the structures needed to
 * perform I/O from a LightWave server.
 */
#ifndef LWSDK_IO_H
#define LWSDK_IO_H

#include <lwtypes.h>


/*
 * Data may be loaded and saved in binary or text files.  Text files
 * are often scene files, and binary files are often object files.
 *
 * In BINARY mode, the external form contains raw bytes having any
 * value from 0 to 255.  Reads and writes are entirely based on the
 * number of bytes requested.
 *
 * In ASCII mode, all data bytes must be in the extended ASCII range
 * of 32 to 255.  Values outside this range are ignored or undefined.
 */
#define LWIO_BINARY      0
#define LWIO_ASCII       1
#define LWIO_OBJECT      LWIO_BINARY
#define LWIO_SCENE       LWIO_ASCII


/*
 * Blocks of data can be marked with identifiers.  In BINARY mode the
 * four-byte ID code is used, and in ASCII mode the string token (which
 * should not contain spaces) is used.
 */
typedef struct st_LWBlockIdent {
        LWID             id;
        const char      *token;
} LWBlockIdent;


/*
 * This structure is used when the server is writing its state to
 * the external store.  The 'writeData' is the first argument to all
 * the callback functions.
 *
 * write        execute a raw write to the store.  In BINARY mode, the
 *              number of bytes indicated by 'len' is written directly to 
 *              the output.  In ASCII mode the input buffer is treated as 
 *              a null-terminated string and the length is computed from 
 *              that.  The string is written with a newline at the end.
 *
 * writeI1      write an array of different sized and typed integers to
 * writeI2      the store.  In BINARY mode the 'n' numbers are written as
 * writeI4      1, 2 or 4 btype elements in "big-endian" (Motorola) 
 * writeU1      format.  In ASCII mode, all 'n' numbers are written to a 
 * writeU2      single line, the signed values in decimal and the unsigned 
 * writeU4      values in hexadecimal.  A newline is written after the 
 *              list of numbers unless the writing is done inside a leaf
 *              block.
 *
 * writeStr     write a null-terminated string to the store.  In ASCII
 *              mode the string may be contained in double quotes.
 *
 * writeID      write an identifier token to the store.  In BINARY mode
 *              this is the four-byte ID code, while in ASCII mode this
 *              is the string token.
 *
 * beginBlk     start a nested data block.  The block is identified by
 *              the ID codes defined in the LWBlockIdent, and the 'leaf'
 *              flag is true if this block will not contain other blocks.
 *
 * endBlk       end the current data block.
 *
 * depth        return the current block nesting level, where zero means
 *              we've entered no blocks.
 */
typedef struct st_LWSaveState {
        int               ioMode;
        void             *writeData;
        void            (*write)    (void *data, const char  *, int len);

        void            (*writeI1)  (void *data, const char  *, int n);
        void            (*writeI2)  (void *data, const short *, int n);
        void            (*writeI4)  (void *data, const long  *, int n);
        void            (*writeU1)  (void *data, const unsigned char  *, int n);
        void            (*writeU2)  (void *data, const unsigned short *, int n);
        void            (*writeU4)  (void *data, const unsigned long  *, int n);
        void            (*writeFP)  (void *data, const float *, int n);
        void            (*writeStr) (void *data, const char  *);
        void            (*writeID)  (void *data, const LWBlockIdent *);

        void            (*beginBlk) (void *data, const LWBlockIdent *, int leaf);
        void            (*endBlk)   (void *data);
        int             (*depth)    (void *data);
} LWSaveState;


/*
 * This structure is used when the server is reading its state from
 * the external store.  The 'readData' is the first argument to all
 * the callback functions.
 *
 * read         execute a raw read of the store.  In binary mode, 'len'
 *              bytes are read directly from the store.  In ASCII mode, up 
 *              to 'len' bytes of the current line are read from the 
 *              store, perhaps leaving more bytes to be read later.  The
 *              return value is the number of bytes read on the current
 *              line (which may be zero), or -1 for end of data.
 *
 * readI1       read an array of integers of various types and formats,
 * readI2       returning the number of integers successfully read.  In
 * readI4       BINARY mode a sequence of 'n' single, double or quad bytes
 * readU1       are read from the stream and interpreted as "big-endian"
 * readU2       (Motorola) format words.  In ASCII mode, the numbers are
 * readU4       read from the current line, in decimal for signed values 
 *              and in hexadecimal for unsigned values.
 *
 * readFP       read an array of floating point numbers from the store.
 *              The return value is the number of values read.
 *
 * readStr      read a string from the store.  In ASCII mode double quotes
 *              will be removed.
 *
 * readID       read an identifier token from the store.  In BINARY this is
 *              just a four-byte code value, but in ASCII this in the string
 *              token which will be matched with its longword code.
 *
 * findBlk      read ahead looking for the next block.  The array of block
 *              identifiers includes all the blocks that could be expected
 *              and is terminated with a null ID code.  If a reconginized
 *              block is found in the file, it's ID code is returned.  A
 *              zero return indicates no more valid blocks.
 *
 * endBlk       complete reading the current open block.
 *
 * depth        return the current block nesting level, where zero means
 *              we've entered no blocks.
 */
typedef struct st_LWLoadState {
        int               ioMode;
        void             *readData;
        int             (*read)    (void *data, char  *, int len);

        int             (*readI1)  (void *data, char  *, int n);
        int             (*readI2)  (void *data, short *, int n);
        int             (*readI4)  (void *data, long  *, int n);
        int             (*readU1)  (void *data, unsigned char  *, int n);
        int             (*readU2)  (void *data, unsigned short *, int n);
        int             (*readU4)  (void *data, unsigned long  *, int n);
        int             (*readFP)  (void *data, float *, int n);
        int             (*readStr) (void *data, char  *, int max);
        LWID            (*readID)  (void *data, const LWBlockIdent *);

        LWID            (*findBlk) (void *data, const LWBlockIdent *);
        void            (*endBlk)  (void *data);
        int             (*depth)   (void *data);
} LWLoadState;


/*
 * Macros make the call format easier to write.
 */
#define LWSAVE_I1(lwss,p,n)     (*(lwss)->writeI1)((lwss)->writeData,p,n)
#define LWSAVE_I2(lwss,p,n)     (*(lwss)->writeI2)((lwss)->writeData,p,n)
#define LWSAVE_I4(lwss,p,n)     (*(lwss)->writeI4)((lwss)->writeData,p,n)
#define LWSAVE_U1(lwss,p,n)     (*(lwss)->writeU1)((lwss)->writeData,p,n)
#define LWSAVE_U2(lwss,p,n)     (*(lwss)->writeU2)((lwss)->writeData,p,n)
#define LWSAVE_U4(lwss,p,n)     (*(lwss)->writeU4)((lwss)->writeData,p,n)
#define LWSAVE_FP(lwss,p,n)     (*(lwss)->writeFP)((lwss)->writeData,p,n)
#define LWSAVE_STR(lwss,p)      (*(lwss)->writeStr)((lwss)->writeData,p)
#define LWSAVE_ID(lwss,b)       (*(lwss)->writeID)((lwss)->writeData,b)
#define LWSAVE_BEGIN(lwss,b,l)  (*(lwss)->beginBlk)((lwss)->writeData,b,l)
#define LWSAVE_END(lwss)        (*(lwss)->endBlk)((lwss)->writeData)
#define LWSAVE_DEPTH(lwss)      (*(lwss)->depth)((lwss)->writeData)

#define LWLOAD_I1(lwls,p,n)     (*(lwls)->readI1)((lwls)->readData,p,n)
#define LWLOAD_I2(lwls,p,n)     (*(lwls)->readI2)((lwls)->readData,p,n)
#define LWLOAD_I4(lwls,p,n)     (*(lwls)->readI4)((lwls)->readData,p,n)
#define LWLOAD_U1(lwls,p,n)     (*(lwls)->readU1)((lwls)->readData,p,n)
#define LWLOAD_U2(lwls,p,n)     (*(lwls)->readU2)((lwls)->readData,p,n)
#define LWLOAD_U4(lwls,p,n)     (*(lwls)->readU4)((lwls)->readData,p,n)
#define LWLOAD_FP(lwls,p,n)     (*(lwls)->readFP)((lwls)->readData,p,n)
#define LWLOAD_STR(lwls,p,n)    (*(lwls)->readStr)((lwls)->readData,p,n)
#define LWLOAD_ID(lwls,b)       (*(lwls)->readID)((lwls)->readData,b)
#define LWLOAD_FIND(lwls,b)     (*(lwls)->findBlk)((lwls)->readData,b)
#define LWLOAD_END(lwls)        (*(lwls)->endBlk)((lwls)->readData)
#define LWLOAD_DEPTH(lwls)      (*(lwls)->depth)((lwls)->readData)


#define LWFILEIOFUNCS_GLOBAL    "File IO"
#define LWIO_BINARY_IFF          0x8C0000

typedef struct st_LWFileIOFuncs {
        LWSaveState *           (*openSave) (const char *name, int ioMode);
        void                    (*closeSave)(LWSaveState *save); 
        LWLoadState *           (*openLoad) (const char *name, int ioMode);
        void                    (*closeLoad)(LWLoadState *load); 
} LWFileIOFuncs;

#endif