/****************************************************************************
 * 
 * Memory 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"

#if (!defined(DOXYGEN))
static const char rcsid[] __RWUNUSED__ = 
"@@(#)$Id: babinary.c,v 1.133 2001/07/16 15:33:35 johns Exp $";
#endif /* (!defined(DOXYGEN)) */

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

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

typedef struct __rwMark _rwMark;

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

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

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

#if (defined(rwLITTLEENDIAN) && !defined(RWDEBUG))
#if (!(defined(__RWUNUSEDRELEASELITTLEENDIAN__)))
#define __RWUNUSEDRELEASELITTLEENDIAN__ __RWUNUSED__
#endif /* (!(defined(__RWUNUSEDRELEASELITTLEENDIAN__))) */
#endif /* (defined(rwLITTLEENDIAN) && !defined(RWDEBUG)) */

#if (!(defined(__RWUNUSEDRELEASELITTLEENDIAN__)))
#define __RWUNUSEDRELEASELITTLEENDIAN__ /* No op */
#endif /* (!(defined(__RWUNUSEDRELEASELITTLEENDIAN__))) */

/* 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 */

#define RWSHORTSWAP(x, y)                           \
    (*(RwUInt16 *)(x)) = (((*(RwUInt16 *)(y))>>8) | \
                          ((*(RwUInt16 *)(y))<<8) )


#ifdef rwLITTLEENDIAN

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

#else /* rwLITTLEENDIAN */

#define RWLITTLEENDIANLONG(x, y)    RWLONGSWAP(x, y)
#define RWNATIVELONG(x, y)          RWLONGSWAP(x, y)
#define RWLITTLEENDIANSHORT(x, y)    RWSHORTSWAP(x, y)
#define RWNATIVESHORT(x, y)          RWSHORTSWAP(x, y)

#endif /* rwLITTLEENDIAN */

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

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

static RwBool binaryIssuedVersionWarning = FALSE;

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

   Static functions

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

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

    RWFUNCTION(RWSTRING("ChunkIsComplex"));
    RWASSERT(chunkHeaderInfo);

    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);
}

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

 On entry   : stream
            : stream chunk id (OUT)
            : stream chunk length (OUT)
            : stream chunk version (OUT)
 On exit    : TRUE if found, else FALSE
 */

RwBool
_rwStreamReadChunkHeader(RwStream * stream, RwUInt32 * type,
                        RwUInt32 * length, RwUInt32 * version)
{
    RwBool              status;
    _rwMark             mark;
    RWFUNCTION(RWSTRING("_rwStreamReadChunkHeader"));
    RWASSERT(stream);

    /* Read it */
    status = ( RwStreamRead(stream, &mark, sizeof(mark)) == sizeof(mark) );
    
    if (!status)
    {
        /* Ooops, failed somewhere */
        RWERROR((E_RW_READ));
        RWRETURN(FALSE);
    }
    else
    {
        /* Convert it */
        RwMemNative(&mark, sizeof(mark));

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

        RWRETURN(TRUE);
    }
    
}

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);

    /* This function replaces RwStreamWriteChunkHeader(), 
     * at end of file */
    /* 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);
}

/**
 * \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_ATOMIC - Search for an atomic (type RpAtomic).
 *              \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_IMAGE - Search for an image (type RwImage).
 *              \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_TEXDICTIONARY - Search for a texture dictionary (type RwTexDictionary).
 *              \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)
{
    RwUInt32            readType;
    RwUInt32            readLength;
    RwUInt32            readVersion;

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

    while (_rwStreamReadChunkHeader(stream, &readType, 
                                   &readLength, &readVersion))
    {
        if (readType == type)
        {
            const RwBool valid = ( (readVersion >= rwLIBRARYBASEVERSION) &&
                                   (readVersion <= rwLIBRARYCURRENTVERSION) );
            const RwBool issue = ( (!binaryIssuedVersionWarning) &&
                                   (readVersion < rwLIBRARYCURRENTVERSION) );

            /* Reject invalid versions */
            if (!valid)
            {
                RWERROR((E_RW_BADVERSION));
                RWRETURN(FALSE);
            }

            if (issue)
            {
                RWMESSAGE((RWSTRING("readVersion 0x%0x < rwLIBRARYCURRENTVERSION 0x%x; content may be sub-optimal"),
                           (unsigned int)readVersion,
                           rwLIBRARYCURRENTVERSION));
                binaryIssuedVersionWarning |= issue;
            }

            /* Found it, return the values */
            if (lengthOut)
            {
                *lengthOut = readLength;
            }
            if (versionOut)
            {
                *versionOut = readVersion;
            }

            RWRETURN(TRUE);
        }

        if (!RwStreamSkip(stream, readLength))
        {
            RWRETURN(FALSE);
        }
    }

    /* Oh, well, the chunk hasn't been found */
    RWRETURN(FALSE);
}


/**
 * \ingroup rwmem
 * \ref RwMemLittleEndian32 converts a specified chunk of memory 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  Amount of memory to prepare (multiple of 4 bytes).
 *
 * \return Returns pointer to the converted memory (converted in situ) on
 * success, or NULL on failure.
 *
 * \see RwMemNative32
 * \see RwMemFloat32ToReal
 * \see RwMemRealToFloat32
 * \see RwStreamReadInt32
 * \see RwStreamWriteInt32
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
void               *
RwMemLittleEndian32(void *mem, 
                    RwUInt32 __RWUNUSEDRELEASELITTLEENDIAN__ size)
{

    RWAPIFUNCTION(RWSTRING("RwMemLittleEndian32"));
    RWASSERT(mem);
    RWASSERT((size & 0x00000003L) == 0);

#ifdef rwBIGENDIAN
    {
        RwUInt32           *memInt = (RwUInt32 *) mem;

        for (size >>= 2 /* Divide by 4 */ ; size > 0; size--)
        {
            RWLITTLEENDIANLONG(memInt, memInt);
            memInt++;
        }
    }

#endif /* rwBIGENDIAN */

    RWRETURN(mem);
}

/**
 * \ingroup rwmem
 * \ref RwMemLittleEndian16 converts a specified chunk of memory 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  Amount of memory to prepare (multiple of 2 bytes).
 *
 * \return Returns pointer to the converted memory (converted in situ) on
 * success, or NULL on failure.
 *
 * \see RwMemNative16
 * \see RwMemFloat32ToReal
 * \see RwMemRealToFloat32
 * \see RwStreamReadInt16
 * \see RwStreamWriteInt16
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
void               *
RwMemLittleEndian16(void *mem, 
                    RwUInt32 __RWUNUSEDRELEASELITTLEENDIAN__ size )
{

    RWAPIFUNCTION(RWSTRING("RwMemLittleEndian16"));
    RWASSERT(mem);
    RWASSERT((size & 1) == 0);

#ifdef rwBIGENDIAN
    {
        RwUInt16           *memInt = (RwUInt16 *) mem;

        for (size >>= 1 /* Divide by 2 */ ; size > 0; size--)
        {
            RWLITTLEENDIANSHORT(memInt, memInt);
            memInt++;
        }
    }

#endif /* rwLITTLEENDIAN */

    RWRETURN(mem);
}

/**
 * \ingroup rwmem
 * \ref RwMemNative32 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 (multiple of 4).
 *
 * \return pointer to the converted memory (converted in situ) 
 *        if successful or NULL if there is an error.
 *
 * \see RwMemLittleEndian32
 * \see RwMemFloat32ToReal
 * \see RwMemRealToFloat32
 * \see RwStreamReadInt32
 * \see RwStreamWriteInt32
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 */
void               *
RwMemNative32(void *mem, 
              RwUInt32  __RWUNUSEDRELEASELITTLEENDIAN__ size)
{

    RWAPIFUNCTION(RWSTRING("RwMemNative32"));
    RWASSERT(mem);
    RWASSERT((size & 0x00000003L) == 0);

#ifdef rwBIGENDIAN
    {
        RwUInt32           *memInt = (RwUInt32 *) mem;

        for (size >>= 2 /* Divide by 4 */ ; size > 0; size--)
        {
            RWNATIVELONG(memInt, memInt);
            memInt++;
        }
    }

#endif

    RWRETURN(mem);
}

/**
 * \ingroup rwmem
 * \ref RwMemNative16 
 * 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 (multiple of 2).
 *
 * \return pointer to the converted memory (converted in situ) 
 *        if successful or NULL if there is an error.
 *
 * \see RwMemLittleEndian16
 * \see RwMemFloat32ToReal
 * \see RwMemRealToFloat32
 * \see RwStreamReadInt16
 * \see RwStreamWriteInt16
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 */
void               *
RwMemNative16(void *mem, 
              RwUInt32 __RWUNUSEDRELEASELITTLEENDIAN__ size )
{

    RWAPIFUNCTION(RWSTRING("RwMemNative16"));
    RWASSERT(mem);
    RWASSERT((size & 1L) == 0);

#ifdef rwBIGENDIAN
    {
        RwUInt16           *memInt = (RwUInt16 *) mem;

        for (size >>= 1 /* Divide by 2 */ ; size > 0; size--)
        {
            RWNATIVESHORT(memInt, memInt);
            memInt++;
        }
    }

#endif /* rwBIGENDIAN */

    RWRETURN(mem);
}

/**
 * \ingroup rwmem
 * \ref RwMemRealToFloat32 converts 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 32-bit floats.
 * \param size  A RwUInt32 value equal to the size in bytes of the array of floats
 *       (should be a multiple of 4).
 *
 * \return Returns the converted array (converted in situ) if successful or 
 *        NULL if there is an error.
 *
 * \see RwMemFloat32ToReal
 * \see RwMemLittleEndian32
 * \see RwMemNative32
 * \see RwStreamReadInt32
 * \see RwStreamWriteInt32
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
void               *
RwMemRealToFloat32(void *mem, RwUInt32 size)
{
    RwUInt32           *memInt = (RwUInt32 *) mem;

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

    for ( size >>= 2 /* Divide by 4 */ ; size > 0; size--)
    {
        RWFLOAT32FROMREAL(memInt, memInt);
        memInt++;
    }

    RWRETURN(mem);
}

/**
 * \ingroup rwmem
 * \ref RwMemFloat32ToReal is used to convert an array of 32-bit floats 
 * to an array of reals. This allows portability between machines that 
 * 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 RwMemRealToFloat32
 * \see RwMemLittleEndian32
 * \see RwMemNative32
 * \see RwStreamRead
 * \see RwStreamReadInt32
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamWriteInt32
 * \see RwStreamWriteReal
 *
 */
void               *
RwMemFloat32ToReal(void *mem, RwUInt32 size)
{
    RwUInt32           *memInt = (RwUInt32 *) mem;

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

    for (size >>= 2 /* Divide by 4 */ ; size > 0; size--)
    {
        RWREALFROMFLOAT32(memInt, memInt);
        memInt++;
    }

    RWRETURN(mem);
}

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

   Binary file format

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

   Simplifying functions

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

/**
 * \ingroup rwstream
 * \ref RwStreamWriteReal is used to write the given array of RwReal 
 * data values to the specified stream. For compatibility across multiple
 * platforms, the data is automatically converted to little-endian format
 * if necessary. This process is reversed upon reading the stream data so 
 * no platform-specific conversion work is needed.
 *
 * \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 RwStreamWriteInt32
 * \see RwStreamReadInt32
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream *
RwStreamWriteReal(RwStream *stream, const RwReal *reals, RwUInt32 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((RwStream *)NULL);
        }

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

    RWRETURN(stream);
}

/**
 * \ingroup rwstream
 * \ref RwStreamWriteInt32 is used to write the given array of RwInt32 
 * data values to the specified stream. For compatibility across multiple
 * platforms, the data is automatically converted to little-endian format
 * if necessary. This process is reversed upon reading the stream data so 
 * no platform-specific conversion is needed.
 *
 * \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 RwStreamReadInt32
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream *
RwStreamWriteInt32(RwStream *stream, const RwInt32 *ints, RwUInt32 numBytes)
{
    RWAPIFUNCTION(RWSTRING("RwStreamWriteInt32"));
    RWASSERT(stream);
    RWASSERT(ints);
    RWASSERT((numBytes & 0x00000003) == 0);

#ifdef rwLITTLEENDIAN
    RwStreamWrite(stream, ints, numBytes);
#else /* rwLITTLEENDIAN */   
    while (numBytes)
    {
        RwUInt8     convertBuffer[256];
        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);
    }
#endif /* rwLITTLEENDIAN */

    RWRETURN(stream);
}

/**
 * \ingroup rwstream
 * \ref RwStreamWriteInt16 is used to write the given array of RwInt16 
 * data values to the specified stream. For compatibility across multiple
 * platforms, the data is automatically converted to little-endian format
 * if necessary. This process is reversed upon reading the stream data so 
 * no platform-specific conversion is needed.
 *
 * \param stream  Pointer to the stream to write to.
 * \param ints  Pointer to array of RwInt16 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 RwStreamReadInt16
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream *
RwStreamWriteInt16(RwStream *stream, const RwInt16 *ints, RwUInt32 numBytes)
{

    RWAPIFUNCTION(RWSTRING("RwStreamWriteInt16"));
    RWASSERT(stream);
    RWASSERT(ints);
    RWASSERT((numBytes&1L) == 0);

#ifdef rwLITTLEENDIAN
    RwStreamWrite(stream, ints, numBytes);
#else /* rwLITTLEENDIAN */
    while (numBytes)
    {
        RwUInt8     convertBuffer[256];
        RwUInt32    bytesToWrite = (numBytes >= 256) ? 256 : numBytes;

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

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

        numBytes -= bytesToWrite;
        ints = (const RwInt16 *) (((const RwUInt8 *) ints) + bytesToWrite);
    }
#endif /* rwLITTLEENDIAN */

    RWRETURN(stream);
}

/**
 * \ingroup rwstream
 * \ref RwStreamReadInt16 is used to read an array of RwInt16 
 * data values from the specified stream. The data is read from the stream
 * in and converted automatically to little-endian or big-endian 
 * representation, as appropriate for the platform.
 * (RenderWare Streams use little-endian format internally.)
 *
 * \param stream  Pointer to the stream to read from.
 * \param ints  Pointer to an array of RwInt16 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 RwStreamWriteInt16
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream *
RwStreamReadInt16(RwStream *stream, RwInt16 *ints, RwUInt32 numBytes)
{
    RWAPIFUNCTION(RWSTRING("RwStreamReadInt16"));
    RWASSERT(stream);
    RWASSERT(ints);
    RWASSERT((numBytes & 1L) == 0);
    
    if (!RwStreamRead(stream, ints, numBytes))
    {
        RWERROR((E_RW_READ));
        RWRETURN((RwStream *)NULL);
    }

    RwMemNative16(ints, numBytes);

    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
 * and converted automatically to little-endian or big-endian format as 
 * appropriate for the platform.
 * (RenderWare Streams use little-endian format internally.)
 *
 * \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 RwStreamWriteInt32
 * \see RwStreamReadInt32
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream *
RwStreamReadReal(RwStream *stream, RwReal *reals, RwUInt32 numBytes)
{
    RWAPIFUNCTION(RWSTRING("RwStreamReadReal"));
    RWASSERT(stream);
    RWASSERT(reals);

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

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

    RWRETURN(stream);
}

/**
 * \ingroup rwstream
 * \ref RwStreamReadInt32 is used to read an array of RwInt32 
 * data values from the specified stream. The data is read from the stream
 * in and converted automatically to little-endian or big-endian 
 * representation, as appropriate for the platform.
 * (RenderWare Streams use little-endian format internally.)
 *
 * \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 RwStreamWriteInt32
 * \see RwStreamWriteReal
 * \see RwStreamReadReal
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream *
RwStreamReadInt32(RwStream *stream, RwInt32 *ints, RwUInt32 numBytes)
{
    RWAPIFUNCTION(RWSTRING("RwStreamReadInt32"));
    RWASSERT(stream);
    RWASSERT(ints);

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

    RwMemNative(ints, numBytes);

    RWRETURN(stream);
}

/**
 * \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)
{
    RwUInt32            readType, readLength, readVersion;

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

    if (!_rwStreamReadChunkHeader(stream, &readType, &readLength, &readVersion))
    {
        RWRETURN((RwStream *)NULL);
    }

    chunkHeaderInfo->type = readType;
    chunkHeaderInfo->length = readLength;
    chunkHeaderInfo->version = readVersion;
    chunkHeaderInfo->isComplex = ChunkIsComplex(chunkHeaderInfo);

    RWRETURN(stream);
}

#if (defined(DOXYGEN))
/* 
 * This function is now a #define that uses 
 * _rwStreamWriteVersionedChunkHeader() 
 */

/**
 * \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. 
 * 
 * The chunk identifier is a 32 bit value which identifies the type of data
 * to be written in a universally unique way. The three highest order bytes
 * (24 most significant bits) should be the developers ID.  The developers ID 
 * is the number that is used by that developer as a username to log on to the 
 * RenderWare developers website. The low-order byte (8 least significant
 * bits) is used for each custom chunk type, and are allocated by the 
 * individual developer. If more than 256 custom data types are required
 * then Criterion would then supply you with further developers IDs.
 *
 * RwStreamWriteChunkHeader is currently implemented as a macro.
 *
 * \param stream  Pointer to the stream to write chunk header to.
 * \param type  A RwInt32 value equal to the chunk's identifier cast to an RwInt32 type.
 * \param size  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 RwStreamWriteInt32
 * \see RwStreamReadInt32
 * \see RwStreamWrite
 * \see RwStreamRead
 *
 */
RwStream           *
RwStreamWriteChunkHeader(RwStream * stream, RwInt32 type, RwInt32 size)
{
    RwStream           *result = NULL;

    RWAPIFUNCTION(RWSTRING("RwStreamWriteChunkHeader"));

    RWRETURN(result);
}
#endif /* (defined(DOXYGEN)) */



