/*
 * Binary stream handling functions.
 * 
 * Copyright (c) 1998 Criterion Software Ltd.
 *
 */

/****************************************************************************
 Includes
 */

#include <stdlib.h>
#include <string.h>

#include "batypes.h"
#include "bastream.h"
#include "balibtyp.h"
#include "batype.h"
#include "badebug.h"
#include "bamemory.h"
#include "babinary.h"

static const char rcsid[] __RWUNUSED__ = 
    "@@(#)$Id: babinasy.c,v 1.17 2001/07/16 15:33:39 johns Exp $";


/****************************************************************************
 Local Types
 */

/* types to be written off to a stream */

typedef struct __rwMark _rwMark;
struct __rwMark
{
        RwUInt32            type;
        RwUInt32            length;
        RwUInt32            version;
};

typedef struct _rwStreamRdChnkHeadInfo rwStreamRdChnkHeadInfo;
struct _rwStreamRdChnkHeadInfo
{
        _rwMark             mark;
        RwUInt32           *type;
        RwUInt32           *length;
        RwUInt32           *version;
};

typedef struct _rwStreamFindChunkInfo rwStreamFindChunkInfo;
struct _rwStreamFindChunkInfo
{
        RwUInt32            state;
        RwUInt32            type;
        RwUInt32            readType;
        RwUInt32            readLength;
        RwUInt32            readVersion;
        RwUInt32           *lengthOut;
        RwUInt32           *versionOut;
};



/****************************************************************************
 Local (Static) Prototypes
 */

/****************************************************************************
 Local Defines
 */

/* A float 32 is a LITTLE ENDIAN IEEE float!!!!! */
#ifndef RwFloat32
#define RwFloat32 float
#endif /* FLOAT32 */

#ifndef RWFLOAT32FROMREAL
#define RWFLOAT32FROMREAL(x, y) ((*(RwFloat32 *)(x))=((*(RwReal *)(y))))
#endif /* RWFLOAT32FROMREAL */

#ifndef RWREALFROMFLOAT32
#define RWREALFROMFLOAT32(x, y) (*(RwReal *)(x))=(RwReal)((*(RwFloat32 *)(y)))
#endif /* RWREALFROMFLOAT32 */

#ifndef RWLONGSWAP
#define RWLONGSWAP(x, y)                                        \
   (*(RwUInt32 *)(x)) = (((*(RwUInt32 *)(y))>>24) |             \
                         (((*(RwUInt32 *)(y))>>8)&0xff00UL) |   \
                         (((*(RwUInt32 *)(y))<<8)&0xff0000UL) | \
                         ((*(RwUInt32 *)(y)<<24)))
#endif /* LONG_SWAP */

#ifdef rwLITTLEENDIAN

#define RWLITTLEENDIANLONG(x, y)    (*(RwUInt32 *)x) = (*(RwUInt32 *)y)
#define RWNATIVELONG(x, y)          (*(RwUInt32 *)x) = (*(RwUInt32 *)y)

#else /* rwLITTLEENDIAN */

#define RWLITTLEENDIANLONG(x, y)    RWLONGSWAP(x, y)
#define RWNATIVELONG(x, y)          RWLONGSWAP(x, y)

#endif /* rwLITTLEENDIAN */

/****************************************************************************
 Globals (across program)
 */

/****************************************************************************
 Local Globals
 */

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Static functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/**
 * \ingroup rwmem
 * \ref RwMemLittleEndian is used to convert the specified chunk of 
 * memory from native format to a little endian format suitable for writing
 * out to a binary file. The binary format specifies little endian as a
 * standard to permit compatibility across different endian-ness machines.
 *
 * \param mem  Pointer to the memory to convert
 * \param size  A RwUInt32 value equal to the size in bytes of the memory 
 *       (should be a multiple of 4).
 *
 * \return Returns pointer to the converted memory (converted in situ) if 
 *        successful or NULL if there is an error.
 *
 * \see RwStreamRead
 * \see RwStreamReadInt
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamWriteInt
 * \see RwStreamWriteReal
 *
 */

void               *
RwMemLittleEndian(void *mem, RwUInt32 size)
{
    RwUInt32           *memInt = (RwUInt32 *) mem;
    RWAPIFUNCTION(RWSTRING("RwMemLittleEndian"));
    RWASSERT(mem);

    size >>= 2;            /* Divide by 4 */

    for (; size > 0; size--)
    {
        RWLITTLEENDIANLONG(memInt, memInt);
        memInt++;
    }

    RWRETURN(mem);
}


/**
 * \ingroup rwmem
 * \ref RwMemNative is used to convert the specified chunk of memory 
 * from a little endian format to native format after reading the chunk 
 * from a binary file. The binary format specifies little endian as a 
 * standard to permit compatibility across different endian-ness machines.
 * 
 * \param mem  Pointer to the memory.
 * \param size  RwUInt32 value equal to the size in bytes of the memory (should 
 *       be a multiple of 4).
 *
 * \return Returns pointer to the converted memory (converted in situ) if 
 *        successful or NULL if there is an error.
 *
 * \see RwMemFloat32ToReal
 * \see RwMemLittleEndian
 * \see RwMemRealToFloat32
 * \see RwStreamRead
 * \see RwStreamReadInt
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamWriteInt
 * \see RwStreamWriteReal
 *
 */
void               *
RwMemNative(void *mem, RwUInt32 size)
{
    RwUInt32           *memInt = (RwUInt32 *) mem;

    RWAPIFUNCTION(RWSTRING("RwMemNative"));
    RWASSERT(mem);

    size >>= 2;            /* Divide by 4 */

    for (; size > 0; size--)
    {
        RWNATIVELONG(memInt, memInt);
        memInt++;
    }

    RWRETURN(mem);
}

/**
 * \ingroup rwmem
 * \ref RwMemRealToFloat32 is used to convert an array of reals to an 
 * array of 32-bit floats. This allows portability between machines which 
 * represent floating point numbers in different ways.
 *
 * \param mem  An array of reals.
 * \param size  A RwUInt32 value equal to the size in bytes of the array of reals 
 *       (should be a multiple of 4).
 *
 * \return Returns the converted array (converted in situ) if successful or 
 *        NULL if there is an error.
 *
 * \see RwStreamRead
 * \see RwStreamReadInt
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamWriteInt
 * \see RwStreamWriteReal
 *
 */

void               *
RwMemRealToFloat32(void *mem, RwUInt32 size)
{
    RwUInt32           *memInt = (RwUInt32 *) mem;

    RWAPIFUNCTION(RWSTRING("RwMemRealToFloat32"));
    RWASSERT(mem);

    size >>= 2;            /* Divide by 4 */

    for (; size > 0; size--)
    {
        RWFLOAT32FROMREAL(memInt, memInt);
        memInt++;
    }

    RWRETURN(mem);
}

/**
 * \ingroup rwmem
 * \ref RwMemFloat32ToReal converts an array of 32 bit floats to an
 * array of reals.  This allows portability between machines which represent
 * floating point numbers in different ways.
 *
 * \param mem  Pointer to the memory to convert
 * \param size  Amount of memory to prepare (should be a multiple of 4 bytes).
 *
 * \return Returns pointer ot the converted memory (converted in situ) on
 * success, or NULL on failure.
 *
 * \see RwStreamReadInt
 * \see RwStreamWriteInt
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */

void               *
RwMemFloat32ToReal(void *mem, RwUInt32 size)
{
    RwUInt32           *memInt = (RwUInt32 *) mem;

    RWAPIFUNCTION(RWSTRING("RwMemFloat32ToReal"));
    RWASSERT(mem);

    size >>= 2;            /* Divide by 4 */

    for (; size > 0; size--)
    {
        RWREALFROMFLOAT32(memInt, memInt);
        memInt++;
    }

    RWRETURN(mem);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Binary file format

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 _rwStreamReadChunkHeader

 On entry   : stream
            : stream chunk id (OUT)
            : stream chunk length (OUT)
            : stream chunk version (OUT)
 On exit    : TRUE if found, else FALSE
 */
static void
StreamReadChunkHeaderCB(RwStream * stream, RwInt32 ioResult, void *userData)
{
    RwBool              retVal = FALSE;

    RWFUNCTION(RWSTRING("StreamReadChunkHeaderCB"));

    if (ioResult == sizeof(_rwMark))
    {
        rwStreamRdChnkHeadInfo *info = (rwStreamRdChnkHeadInfo *) userData;

        /* Convert it */
        RwMemNative(&info->mark, sizeof(_rwMark));

        /* Use it */
        if (info->type)
        {
            *info->type = info->mark.type;
        }
        if (info->length)
        {
            *info->length = info->mark.length;
        }
        if (info->version)
        {
            *info->version = info->mark.version;
        }

        retVal = TRUE;
    }

    /* Release the context */
    RwStreamReleaseCallback(stream, retVal);
    RWRETURNVOID();
}

RwBool
_rwStreamReadChunkHeader(RwStream * stream, RwUInt32 * type,
                        RwUInt32 * length, RwUInt32 * version)
{
    rwStreamRdChnkHeadInfo *info;

    RWFUNCTION(RWSTRING("_rwStreamReadChunkHeader"));
    RWASSERT(stream);

    info = (rwStreamRdChnkHeadInfo *)
        RwStreamAllocCallback(stream, StreamReadChunkHeaderCB,
                              sizeof(rwStreamRdChnkHeadInfo));

    info->type = type;
    info->length = length;
    info->version = version;

    /* Read it */
    if (RwStreamRead(stream, &info->mark, sizeof(_rwMark)) == sizeof(_rwMark))
    {
        RWRETURN(TRUE);
    }

    /* Ooops, failed somewhere */
    RWRETURN(FALSE);
}


#define RWSTREAMFINDCHUNKSTATEREADHEADER    0
#define RWSTREAMFINDCHUNKSTATESKIPCHUNK     1

/*
 * \ref StreamFindChunkCB is the default "find" call back function for
 * a stream, which is installed by \ref RwStreamFindChunk through an
 * appropriate call to \ref RwStreamAllocCallback.
 * 
 * \param stream for which \ref StreamFindChunkCB has been called
 * \li TRUE - attempt to process chunk
 * \li FALSE - do not attempt to process chunk
 * 
 * \param ioResult  encodes action to perform on chunk
 * \param userdata 
 *
 * \see StreamFindChunkCB
 * \see RwStreamAllocCallback
 */
static void
StreamFindChunkCB(RwStream * stream, RwInt32 ioResult, void *userdata)
{
    rwStreamFindChunkInfo *info = (rwStreamFindChunkInfo *) userdata;
    RWFUNCTION(RWSTRING("StreamFindChunkCB"));

    switch (info->state)
    {
        case RWSTREAMFINDCHUNKSTATEREADHEADER:
            {
                if (ioResult == TRUE)
                {
                    if (info->readType == info->type)
                    {
                        /* Found it, return the values */
                        if (info->lengthOut)
                        {
                            *info->lengthOut = info->readLength;
                        }
                        if (info->versionOut)
                        {
                            *info->versionOut = info->readVersion;
                        }

                        /* Found chunk OK, Finished this function */
                        RwStreamReleaseCallback(stream, TRUE);
                        RWRETURNVOID();
                    }
                    else
                    {
                        info->state = RWSTREAMFINDCHUNKSTATESKIPCHUNK;
                        if (RwStreamSkip(stream, info->readLength))
                        {
                            /* Issued Skip command OK */
                            RWRETURNVOID();
                        }
                    }
                }
            }
            break;
        case RWSTREAMFINDCHUNKSTATESKIPCHUNK:
            {
                if (ioResult == TRUE)
                {
                    info->state = RWSTREAMFINDCHUNKSTATEREADHEADER;
                    if (_rwStreamReadChunkHeader
                        (stream, &info->readType, &info->readLength,
                         &info->readVersion))
                    {
                        /* Issued Chunk Read OK */
                        RWRETURNVOID();
                    }
                }
            }
            break;
    }

    /* Oh, well, the chunk hasn't been found, something went wrong somewhere */
    RwStreamReleaseCallback(stream, FALSE);
    RWRETURNVOID();
}


/**
 * \ingroup rwstream
 * \ref RwStreamFindChunk is used to scan through the specified stream 
 * looking for the first occurence of a chunk of the given type. This function
 * can be used in conjunction with the appropriate stream read function
 * to load objects and data from RenderWare binary files.
 *
 * \param stream  Pointer to the stream to scan through.
 * \param type  A RwUInt32 value equal to the chunk's identifier:
 *              \li rwID_CAMERA - Search for a camera (type RwCamera).
 *              \li rwID_CLUMP - Search for a clump (type RpClump).
 *              \li rwID_GEOMETRY - Search for a geometry (type RpGeometry).
 *              \li rwID_LIGHT - Search for a light (type RpLight).
 *              \li rwID_MATERIAL - Search for a material (type RpMaterial).
 *              \li rwID_MATRIX - Search for a matrix (type RwMatrix).
 *              \li rwID_TEXTURE - Search for a texture (type RwTexture).
 *              \li rwID_WORLD - Search for a world (type RpWorld).
 *
 * \param lengthOut  Pointer to a RwUInt32 value that will receive the length in bytes
 * of the chunk data if it is found. Set to NULL if not required.
 * \param versionOut  Pointer to a RwUInt32 value that will receive the version
 * of the chunk data if it is found. Set to NULL if not required.
 *
 * \return Returns TRUE if the chunk was found or FALSE if not or if there
 * is an error.
 *
 * \see RwCameraStreamRead
 * \see RpClumpStreamRead
 * \see RpGeometryStreamRead
 * \see RpLightStreamRead
 * \see RpMaterialStreamRead
 * \see RwMatrixStreamRead
 * \see RwTextureStreamRead
 * \see RpWorldStreamRead
 * \see RwStreamWriteChunkHeader
 *
 */
RwBool
RwStreamFindChunk(RwStream * stream, RwUInt32 type, RwUInt32 * lengthOut,
                  RwUInt32 * versionOut)
{
    rwStreamFindChunkInfo *info;

    RWAPIFUNCTION(RWSTRING("RwStreamFindChunk"));
    RWASSERT(stream);

    /* Allocate a Context */
    info = (rwStreamFindChunkInfo *)
        RwStreamAllocCallback(stream, RwStreamFindChunkCB,
                              sizeof(rwStreamFindChunkInfo));

    if (!info)
    {
        RWRETURN(FALSE);
    }

    info->state = RWSTREAMFINDCHUNKSTATEREADHEADER;
    info->type = type;
    info->lengthOut = lengthOut;
    info->versionOut = versionOut;

    if (_rwStreamReadChunkHeader(stream, &info->readType, 
                                &info->readLength, &info->readVersion))
    {
        RWRETURN(TRUE);
    }
    else
    {
        RWRETURN(FALSE);
    }
}

RwStream           *
_rwStreamWriteVersionedChunkHeader(RwStream * stream, RwInt32 type,
                                  RwInt32 size, RwUInt32 version)
{
    _rwMark             mark;
    RwStream           *streamRet;

    /* We fake the function name - usually called through a #define */
    RWFUNCTION(RWSTRING("_rwStreamWriteVersionedChunkHeader"));
    RWASSERT(stream);

    /* Fill it */
    mark.type = type;
    mark.length = size;
    mark.version = version;

    /* Convert it */
    RwMemLittleEndian(&mark, sizeof(mark));

    /* Write it */
    streamRet = RwStreamWrite(stream, &mark, sizeof(mark));

    RWRETURN(streamRet);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   Simplifying functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/**
 * \ingroup rwstream
 * \ref RwStreamWriteReal is used to write the given array of RwReal 
 * data values to the specified stream. The data is written to the stream
 * in a platform-independent manner by converting each value from little
 * endian or big endian representation, as appropriate for the platform.
 *
 * \param stream  Pointer to the stream to write to.
 * \param reals  Pointer to array of RwReal data values.
 * \param numBytes  A RwInt32 value equal to the size of the array in bytes. Use the
 * sizeof operator to ensure portability.
 *
 * \return Returns pointer to the stream if successful or NULL if there
 * is an error.
 *
 * \see RwStreamReadReal
 * \see RwStreamWriteInt
 * \see RwStreamReadInt
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream           *
RwStreamWriteReal(RwStream * stream, const RwReal * reals, RwInt32 numBytes)
{
    RwUInt8             convertBuffer[256];

    RWAPIFUNCTION(RWSTRING("RwStreamWriteReal"));
    RWASSERT(stream);
    RWASSERT(reals);

    while (numBytes)
    {
        RwUInt32 bytesToWrite = (numBytes >= 256) ? 256 : numBytes;

        memcpy(convertBuffer, reals, bytesToWrite);
        RwMemRealToFloat32(convertBuffer, bytesToWrite);
        RwMemLittleEndian(convertBuffer, bytesToWrite);

        if (!RwStreamWrite(stream, convertBuffer, bytesToWrite))
        {
            RWRETURN(NULL);
        }

        numBytes -= bytesToWrite;
        reals = (const RwReal *) (((const RwUInt8 *) reals) + bytesToWrite);
    }

    RWRETURN(stream);
}

/**
 * \ingroup rwstream
 * \ref RwStreamWriteInt is used to write the given array of RwInt32 
 * data values to the specified stream. The data is written to the stream
 * in a platform-independent manner by converting each value from little
 * endian or big endian representation, as appropriate for the platform.
 *
 * \param stream  Pointer to the stream to write to.
 * \param ints  Pointer to array of RwInt32 data values.
 * \param numBytes  A RwInt32 value equal to the size of the array in bytes. Use the
 * sizeof operator to ensure portability.
 *
 * \return Returns pointer to the stream if successful or NULL if there
 * is an error.
 *
 * \see RwStreamReadInt
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream           *
RwStreamWriteInt(RwStream * stream, const RwInt32 * ints, RwInt32 numBytes)
{
    RwUInt8             convertBuffer[256];

    RWAPIFUNCTION(RWSTRING("RwStreamWriteInt"));
    RWASSERT(stream);
    RWASSERT(ints);

    while (numBytes)
    {
        RwUInt32 bytesToWrite = (numBytes >= 256) ? 256 : numBytes;

        memcpy(convertBuffer, ints, bytesToWrite);
        RwMemLittleEndian(convertBuffer, bytesToWrite);

        if (!RwStreamWrite(stream, convertBuffer, bytesToWrite))
        {
            RWRETURN(NULL);
        }

        numBytes -= bytesToWrite;
        ints = (const RwInt32 *) (((const RwUInt8 *) ints) + bytesToWrite);
    }

    RWRETURN(stream);
}

/**
 * \ingroup rwstream
 * \ref RwStreamReadReal is used to read an array of RwReal 
 * data values from the specified stream. The data is read from the stream
 * in a platform-independent manner by converting each value to little
 * endian or big endian representation, as appropriate for the platform.
 *
 * \param stream  Pointer to the stream to read from.
 * \param reals  Pointer to an array of RwReal values that will receive the
 * data from the stream.
 * \param numBytes  A RwInt32 value equal to the size of the array in bytes. Use the
 * sizeof operator to ensure portability.
 *
 * \return Returns pointer to the stream if successful or NULL if there
 * is an error.
 *
 * \see RwStreamWriteReal
 * \see RwStreamWriteInt
 * \see RwStreamReadInt
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream           *
RwStreamReadReal(RwStream * stream, RwReal * reals, RwInt32 numBytes)
{
    RWAPIFUNCTION(RWSTRING("RwStreamReadReal"));
    RWASSERT(stream);
    RWASSERT(reals);

    /* We can pull this straight into the input buffer - it's not const */
    if (((RwUInt32) numBytes) != RwStreamRead(stream, reals, (RwUInt32) numBytes))
    {
        RWRETURN(NULL);
    }

    RwMemNative(reals, (RwUInt32) numBytes);
    RwMemFloat32ToReal(reals, (RwUInt32) numBytes);

    RWRETURN(stream);
}

/**
 * \ingroup rwstream
 * \ref RwStreamReadInt is used to read an array of RwInt32 
 * data values from the specified stream. The data is read from the stream
 * in a platform-independent manner by converting each value to little
 * endian or big endian representation, as appropriate for the platform.
 *
 * \param stream  Pointer to the stream to read from.
 * \param ints  Pointer to an array of RwInt32 values that will receive the
 * data from the stream.
 * \param numBytes  A RwInt32 value equal to the size of the array in bytes. Use the
 * sizeof operator to ensure portability.
 *
 * \return Returns pointer to the stream if successful or NULL if there
 * is an error.
 *
 * \see RwStreamWriteInt
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream           *
RwStreamReadInt(RwStream * stream, RwInt32 * ints, RwInt32 numBytes)
{
    RWAPIFUNCTION(RWSTRING("RwStreamReadInt"));
    RWASSERT(stream);
    RWASSERT(ints);

    /* We can pull this straight into the input buffer - it's not const */
    if (((RwUInt32) numBytes) != RwStreamRead(stream, ints, (RwUInt32) numBytes))
    {
        RWRETURN(NULL);
    }

    RwMemNative(ints, (RwUInt32) numBytes);

    RWRETURN(stream);
}

static              RwBool
ChunkIsComplex(RwChunkHeaderInfo * chunkHeaderInfo)
{
    RwBool              result = FALSE;

    RWFUNCTION(RWSTRING("ChunkIsComplex"));

    switch (chunkHeaderInfo->type)
    {

        case rwID_STRUCT:
            {
                result = (FALSE);
                break;
            }

        case rwID_STRING:
            {
                result = (FALSE);
                break;
            }

        case rwID_EXTENSION:
            {
                result = (FALSE);
                break;
            }

        case rwID_CAMERA:
            {
                result = (TRUE);
                break;
            }

        case rwID_TEXTURE:
            {
                result = (TRUE);
                break;
            }

        case rwID_MATERIAL:
            {
                result = (TRUE);
                break;
            }

        case rwID_MATLIST:
            {
                result = (TRUE);
                break;
            }

        case rwID_ATOMICSECT:
            {
                result = (TRUE);
                break;
            }

        case rwID_PLANESECT:
            {
                result = (TRUE);
                break;
            }

        case rwID_WORLD:
            {
                result = (TRUE);
                break;
            }

        case rwID_MATRIX:
            {
                result = (FALSE);
                break;
            }

        case rwID_FRAMELIST:
            {
                result = (TRUE);
                break;
            }

        case rwID_GEOMETRY:
            {
                result = (TRUE);
                break;
            }

        case rwID_CLUMP:
            {
                result = (TRUE);
                break;
            }

        case rwID_LIGHT:
            {
                result = (TRUE);
                break;
            }

        case rwID_UNICODESTRING:
            {
                result = (FALSE);
                break;
            }

        case rwID_ATOMIC:
            {
                result = (TRUE);
                break;
            }

        case rwID_GEOMETRYLIST:
            {
                result = (TRUE);
                break;
            }

        default:
            {
                result = (FALSE);
                break;
            }
    }

    RWRETURN(result);
}

/*********************************************************************
 * _func \ref StreamReadChunkHeaderInfoCB
 */
static void
StreamReadChunkHeaderInfoCB(RwStream * stream, RwInt32 ioResult,
                            void *userdata)
{
    RwInt32             retVal = 0;
    RWFUNCTION(RWSTRING("StreamReadChunkHeaderInfoCB"));
    RWASSERT(stream);

    if (ioResult == TRUE)
    {
        RwChunkHeaderInfo  *chunkHeaderInfo = *(RwChunkHeaderInfo **) userdata;
        chunkHeaderInfo->isComplex = ChunkIsComplex(chunkHeaderInfo);

        retVal = (RwInt32) stream;
    }

    /* Release our context */
    RwStreamReleaseCallback(stream, retVal);

    RWRETURNVOID();
}

/*********************************************************************/

/**
 * \ingroup rwstream
 * \ref RwStreamReadChunkHeaderInfo reads the Chunk Header information and
 * stores it in the structure provided.
 * 
 * \param stream  Pointer to stream to extract information from
 * \param chunkHeaderInfo  Pointer to storage for chunkHeaderInfo datatype. 
 * 
 * \return Pointer to the stream if successful; NULL if an error occurred
 * 
 * \see RwStreamOpen
 * \see RwStreamRead
 */
RwStream           *
RwStreamReadChunkHeaderInfo(RwStream * stream,
                            RwChunkHeaderInfo * chunkHeaderInfo)
{
    RwChunkHeaderInfo **chunkInfo;

    RWAPIFUNCTION(RWSTRING("RwStreamReadChunkHeaderInfo"));
    RWASSERT(stream);
    RWASSERT(chunkHeaderInfo);

    /* Set our context for this operation */
    chunkInfo = (RwChunkHeaderInfo **) 
        RwStreamAllocCallback(stream,
                              StreamReadChunkHeaderInfoCB,
                              sizeof(RwChunkHeaderInfo *));

    /* Check we got some space on the stack */
    if (chunkInfo == NULL)
    {
        RWRETURN(NULL);
    }

    /* This is all the data we need to store */
    *chunkInfo = chunkHeaderInfo;

    if (!_rwStreamReadChunkHeader(stream, &chunkHeaderInfo->type, 
                                 &chunkHeaderInfo->length, &chunkHeaderInfo->version))
    {
        RWRETURN(NULL);
    }

    RWRETURN(stream);
}

#if (DOXYGEN)

/**
 * \ingroup rwstream
 * \ref RwStreamWriteChunkHeader is used to write a chunk header to the 
 * specified stream. This function would typically be used by an application
 * wishing to write application specific data to a file. In this case the
 * high-order byte of the type field should be zero to prevent possible
 * conflicts with internal types.
 *
 * \param Pointer to the stream to write chunk header to.
 * \param A RwInt32 value equal to the chunk's identifier.
 * \param A RwInt32 value equal to the size of the chunk data.
 *
 * \return Returns pointer to the stream if successful or NULL if there
 * is an error.
 *
 * \see RwStreamFindChunk
 * \see RwStreamWriteReal
 * \see RwStreamWriteInt
 * \see RwStreamReadInt
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream           *
_rwStreamWriteChunkHeader(RwStream * stream, RwInt32 type, RwInt32 size)
{
    RwStream           *result = NULL;
    RWFUNCTION(RWSTRING("_rwStreamWriteChunkHeader"));

    RWRETURN(result);
}
#endif /* 0 */

