/***************************************************************************
 *                                                                         *
 * Module  : banull.c                                                      *
 *                                                                         *
 * Purpose : Null driver device layer                                      *
 *                                                                         *
 **************************************************************************/

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

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

#include "batypes.h"
#include "badevice.h"
#include "balibtyp.h"
#include "baraster.h"
#include "baimage.h"
#include "batextur.h"
#include "bacamera.h"
#include "baraster.h"
#include "bamemory.h"
#include "barwtyp.h"
#include "baim3d.h"
#include "baresour.h"
#include "p2core.h"


/* String abstraction API for unicode support */
#include "rwstring.h"

/* Drag in the vertex format */
#include "drvmodel.h"

/* This files header */

#include "banull.h"

static const char rcsid[] __RWUNUSED__ = "@@(#)$Id: banull.c,v 1.92 2001/08/31 09:37:57 johns Exp $";

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

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

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

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

/****************************************************************************
 Local (static) Globals
 */

/* The device globals */
RwRwDeviceGlobals dgGGlobals;

/* NOT STATIC!!
 * But is the link from the library side to the device side of the system
 */

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

                          Dummy render functions

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

/****************************************************************************
 _rwNlSetRenderState

 On entry   : State
            : pParam
 On exit    :
 */

static          RwBool
_rwNlSetRenderState(RwRenderState nState __RWUNUSED__,
                    void *pParam __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlSetRenderState"));
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlGetRenderState

 On entry   : State
            : Param
 On exit    : TRUE
 */

static          RwBool
_rwNlGetRenderState(RwRenderState nState __RWUNUSED__,
                    void *pParam __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlGetRenderState"));
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlIm2DRenderLine

 On entry   : verts
 On exit    :
 */

static          RwBool
_rwNlIm2DRenderLine(RwIm2DVertex *verts __RWUNUSED__,
                    RwInt32 numVerts __RWUNUSED__,
                    RwInt32 v1 __RWUNUSED__,
                    RwInt32 v2 __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlIm2DRenderLine"));

#ifdef etimap1000
    /* Cheesy and hacky for now, but the libs get so big with RWDEBUG and MAP that it fails to link! */
    rwprintf(RWSTRING("Draw line from %d,%d to %d,%d\n"),
             (RwInt32) (verts[v1].screen.x),
             (RwInt32) (verts[v1].screen.y),
             (RwInt32) (verts[v2].screen.x),
             (RwInt32) (verts[v2].screen.y));
#endif /* etimap1000 */

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlIm2DRenderTriangle

 On entry   : verts
 On exit    :
 */

static          RwBool
_rwNlIm2DRenderTriangle(RwIm2DVertex *verts __RWUNUSED__,
                        RwInt32 numVerts __RWUNUSED__,
                        RwInt32 v1 __RWUNUSED__,
                        RwInt32 v2 __RWUNUSED__,
                        RwInt32 v3 __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlIm2DRenderTriangle"));

#ifdef etimap1000
    /* Cheesy and hacky for now, but the libs get so big with RWDEBUG and MAP that it fails to link! */
    rwprintf(RWSTRING("Draw triangle from %d,%d to %d,%d to %d,%d\n"),
             (RwInt32) (verts[v1].screen.x),
             (RwInt32) (verts[v1].screen.y),
             (RwInt32) (verts[v2].screen.x),
             (RwInt32) (verts[v2].screen.y),
             (RwInt32) (verts[v3].screen.x),
             (RwInt32) (verts[v3].screen.y));
#endif /* etimap1000 */

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlIm2DRenderPrimitive

 On entry   : verts, indices
 On exit    :
 */

static          RwBool
_rwNlIm2DRenderPrimitive(RwPrimitiveType nPrimType,
                         RwIm2DVertex *tvpVert,
                         RwInt32 nNumVertices)
{
    RwInt32         index;
    RwInt32         nNumIndices = nNumVertices;

    RWFUNCTION(RWSTRING("_rwNlIm2DRenderPrimitive"));

    switch (nPrimType)
    {
        case rwPRIMTYPELINELIST:
            {
                index = 0;
                while (nNumIndices > 1)
                {
                    _rwNlIm2DRenderLine(tvpVert, nNumVertices, index,
                                        index + 1);

                    index += 2;
                    nNumIndices -= 2;
                }

                break;
            }
        case rwPRIMTYPEPOLYLINE:
            {
                index = 0;
                while (nNumIndices > 1)
                {
                    _rwNlIm2DRenderLine(tvpVert, nNumVertices, index,
                                        index + 1);

                    index++;
                    nNumIndices--;
                }

                break;
            }
        case rwPRIMTYPETRILIST:
            {
                index = 0;
                while (nNumIndices > 2)
                {
                    _rwNlIm2DRenderTriangle(tvpVert, nNumVertices,
                                            index, index + 1, index + 2);

                    index += 3;
                    nNumIndices -= 3;
                }

                break;
            }
        case rwPRIMTYPETRISTRIP:
            {
                RwInt32         vertInd = 1;

                index = 0;
                while (nNumIndices > 2)
                {
                    /* Dispatch a polygon */
                    _rwNlIm2DRenderTriangle(tvpVert, nNumVertices,
                                            index, index + vertInd,
                                            index + (vertInd ^ 3));

                    /* Flip the backface test for next time!! This turns 2=>1 and 1=>2 */
                    vertInd ^= 3;

                    /* Next triple of vertices */
                    index++;
                    nNumIndices--;
                }

                break;
            }
        case rwPRIMTYPETRIFAN:
            {
                index = 1;
                while (nNumIndices > 2)
                {
                    _rwNlIm2DRenderTriangle(tvpVert, nNumVertices, 0,
                                            index, index + 1);

                    index++;
                    nNumIndices--;
                }

                break;
            }
        default:
            {
                break;
            }
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlIm2DRenderIndexedPrimitive

 On entry   : verts, indices
 On exit    :
 */

static          RwBool
_rwNlIm2DRenderIndexedPrimitive(RwPrimitiveType nPrimType,
                                RwIm2DVertex *tvpVert,
                                RwInt32 nNumVertices,
                                RwImVertexIndex *indices,
                                RwInt32 nNumIndices)
{
    RWFUNCTION(RWSTRING("_rwNlIm2DRenderIndexedPrimitive"));

    switch (nPrimType)
    {
        case rwPRIMTYPELINELIST:
            {
                while (nNumIndices > 1)
                {
                    _rwNlIm2DRenderLine(tvpVert, nNumVertices,
                                        indices[0], indices[1]);

                    indices += 2;
                    nNumIndices -= 2;
                }

                break;
            }
        case rwPRIMTYPEPOLYLINE:
            {
                while (nNumIndices > 1)
                {
                    _rwNlIm2DRenderLine(tvpVert, nNumVertices,
                                        indices[0], indices[1]);

                    indices++;
                    nNumIndices--;
                }

                break;
            }
        case rwPRIMTYPETRILIST:
            {
                while (nNumIndices > 2)
                {
                    _rwNlIm2DRenderTriangle(tvpVert, nNumVertices,
                                            indices[0], indices[1],
                                            indices[2]);

                    indices += 3;
                    nNumIndices -= 3;
                }

                break;
            }
        case rwPRIMTYPETRISTRIP:
            {
                RwInt32         vertInd = 1;

                nNumIndices -= 2;
                while (nNumIndices > 0)
                {
                    /* Dispatch a polygon */
                    _rwNlIm2DRenderTriangle(tvpVert, nNumVertices,
                                            indices[0],
                                            indices[vertInd],
                                            indices[vertInd
                                                    ^ 3]);

                    /* Flip the backface test for next time!! This turns 2=>1 and 1=>2 */
                    vertInd ^= 3;

                    /* Next triple of vertices */
                    indices++;
                    nNumIndices--;
                }

                break;
            }
        case rwPRIMTYPETRIFAN:
            {
                RwInt32         startVert = indices[0];

                nNumIndices -= 2;

                indices++;

                while (nNumIndices > 0)
                {
                    _rwNlIm2DRenderTriangle(tvpVert, nNumVertices,
                                            startVert, indices[0],
                                            indices[1]);

                    indices++;
                    nNumIndices--;
                }

                break;
            }
        default:
            {
                break;
            }
    }

    RWRETURN(TRUE);
}

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

                        Standard functions

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

/****************************************************************************
 _rwNlCameraBeginUpdate

 On entry   : NULL
            : camera pointer (IN)
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlCameraBeginUpdate(void *pOut __RWUNUSED__,
                       void *pCam __RWUNUSED__,
                       RwInt32 nData __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlCameraBeginUpdate"));

    /* Let's pretend we can do this - we never render anything anyway */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlCameraEndUpdate

 On entry   : NULL
            : camera pointer (IN)
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlCameraEndUpdate(void *pOut __RWUNUSED__,
                     void *pCam __RWUNUSED__,
                     RwInt32 nData __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlCameraEndUpdate"));

    /* Let's pretend we can do this - we never render anything anyway */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlCameraClear

 On entry   : camera pointer (IN)
            : pointer to RwRGBA giving frame buffer color
            : bit field indicating what to clear (rwCAMERACLEARIMAGE,
                                                  rwCAMERACLEARZ)
 On exit    : TRUE on success
 */
static          RwBool
_rwNlCameraClear(void *pCam __RWUNUSED__,
                 void *pCol __RWUNUSED__,
                 RwInt32 nClearWhich __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlCameraClear"));

    /* Let's pretend we can do this - we never render anything anyway */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterShowRaster

 On entry   : raster pointer (IN)
            : device specific pointer (IN) - HWND for windows, eg
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterShowRaster(void *pRas __RWUNUSED__,
                      void *pDev __RWUNUSED__,
                      RwInt32 nData __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlRasterShowRaster"));

    /* Let's pretend we can do this - we never render anthing anyway */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRGBToPixel

 On entry   : palette value (OUT)
            : palette entry (IN)
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRGBToPixel(void *pPixel,
                void *pCol,
                RwInt32 nFormat)
{
    RwRGBA         *rpCol = (RwRGBA *) pCol;

    RWFUNCTION(RWSTRING("_rwNlRGBToPixel"));

    switch (nFormat & (RwInt32) rwRASTERFORMATPIXELFORMATMASK)
    {
        case rwRASTERFORMAT8888:
        case rwRASTERFORMATDEFAULT:
        {
            *((RwUInt32 *) pPixel) =
                (((RwUInt32) rpCol->red) << 24) |
                (((RwUInt32) rpCol->green) << 16) |
                (((RwUInt32) rpCol->blue) << 8) |
                (((RwUInt32) rpCol->alpha) << 0);

            break;
        }
        case rwRASTERFORMAT888:
        {
            *((RwUInt32 *) pPixel) =
                (((RwUInt32) rpCol->red) << 16) |
                (((RwUInt32) rpCol->green) << 8 ) |
                (((RwUInt32) rpCol->blue) << 0);
            break;
        }
        default:
        {
            RWERROR((E_RW_INVRASTERFORMAT));
            RWRETURN(FALSE);
        }
    }

    /* All done */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlPixelToRGB

 On entry   : Palette entry (OUT)
            : NULL
            : Palette entry (IN)
 On exit    : TRUE on success
 */
static          RwBool
_rwNlPixelToRGB(void *pRGB,
                void *pPixel,
                RwInt32 nFormat)
{
    RwRGBA         *rpRGB = (RwRGBA *) pRGB;
    RwInt32         nPixel = *(RwInt32 *) pPixel;

    RWFUNCTION(RWSTRING("_rwNlPixelToRGB"));
    RWASSERT(pPixel);
    RWASSERT(pRGB);

    switch (nFormat & (RwInt32) rwRASTERFORMATPIXELFORMATMASK)
    {
        case rwRASTERFORMAT8888:
        case rwRASTERFORMATDEFAULT:
        {
            rpRGB->red = (RwUInt8) (nPixel >> 24);
            rpRGB->green = (RwUInt8) (nPixel >> 16);
            rpRGB->blue = (RwUInt8) (nPixel >> 8);
            rpRGB->alpha = (RwUInt8) (nPixel >> 0);

            break;
        }
        case rwRASTERFORMAT888:
        {
            rpRGB->red = (RwUInt8) (nPixel >> 16);
            rpRGB->green = (RwUInt8) (nPixel >> 8);
            rpRGB->blue = (RwUInt8) (nPixel >> 0);
            rpRGB->alpha = 0xFF;

            break;
        }
        default:
        {
            RWERROR((E_RW_INVRASTERFORMAT));
            RWRETURN(FALSE);
        }
    }

    RWRETURN(TRUE);
}

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

                         Texture access

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

/****************************************************************************
 _rwNlTextureSetRaster

 On entry   : Texture
            : Raster
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlTextureSetRaster(void *pTex,
                      void *pRas,
                      RwInt32 nIn)
{
    RwTexture      *tex = (RwTexture *) pTex;
    RwRaster       *rpRas = (RwRaster *) pRas;

    RWFUNCTION(RWSTRING("_rwNlTextureSetRaster"));

    /* Stop warnings */
    nIn = nIn;

    /* Try and set the raster */
    tex->raster = rpRas;

    /* All done */
    RWRETURN(TRUE);
}

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

                      Finding the appropriate format

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

/****************************************************************************
 _rwNlGetRasterFormat

 On entry   :
            : Raster
            : Flags
 On exit    :
 */
static          RwBool
_rwNlGetRasterFormat(void *pInOut,
                     void *pRaster,
                     RwInt32 flags)
{
    RwRaster       *rpRas = (RwRaster *) pRaster;
    RwInt32         nFormat = flags & (RwInt32) rwRASTERFORMATMASK;

    RWFUNCTION(RWSTRING("_rwNlGetRasterFormat"));

    /* Stop warnings */
    pInOut = pInOut;

    /* Only 16 bits per pixel supported for Z buffers, 32 bits for all others */

    /* Copy over types */
    rpRas->cType = (RwUInt8) (flags & (RwInt32) rwRASTERTYPEMASK);
    rpRas->cFlags = (RwUInt8) (flags & (RwInt32) (~rwRASTERTYPEMASK));

    /* The types */

    /* If is not a camera buffer, then we need to allocate real memory for it
     */

    switch (rpRas->cType)
    {
        case rwRASTERTYPETEXTURE:
            {
                if ((nFormat & rwRASTERFORMATPIXELFORMATMASK) == rwRASTERFORMATDEFAULT)
                {
                    nFormat |= rwRASTERFORMAT8888;
                    rpRas->depth = 32;
                }
                else
                {
                    if (nFormat & rwRASTERFORMAT888)
                    {
                        rpRas->depth = 24;
                    }
                    else if (nFormat & rwRASTERFORMAT8888)
                    {
                        rpRas->depth = 32;
                    }
                    else
                    {
                        RWERROR((E_RW_INVRASTERFORMAT));
                        RWRETURN(FALSE);
                    }
                }
                rpRas->cFormat = (RwUInt8) (nFormat >> 8);
                break;
            }
        case rwRASTERTYPENORMAL:
        case rwRASTERTYPECAMERA:
            {
                if (!rpRas->depth)
                {
                    rpRas->depth = 32;
                }
                else if (rpRas->depth != 32)
                {
                    RWRETURN(FALSE);
                }

                /* By default textures are 8888 */
                if (nFormat == (RwInt32) rwRASTERFORMATDEFAULT)
                {
                    nFormat = (RwInt32) rwRASTERFORMAT8888;
                }

                rpRas->cFormat = (RwUInt8) (nFormat >> 8);

                break;
            }
        case rwRASTERTYPEZBUFFER:
            {
                if (!rpRas->depth)
                {
                    rpRas->depth = 16;
                }
                else if (rpRas->depth != 16)
                {
                    RWRETURN(FALSE);
                }

                /* By default, Z buffers of 16 bit */
                if (nFormat == (RwInt32) rwRASTERFORMATDEFAULT)
                {
                    nFormat = (RwInt32) rwRASTERFORMAT16;
                }

                rpRas->cFormat = (RwUInt8) (nFormat >> 8);

                break;
            }
        default:
            {
                RWRETURN(FALSE);
            }
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlImageFindFormat

 Finds if an image is or has an alpha channel

 On entry   : Image
            : Raster format (888 if not masked, 8888 otherwise) (OUT)
 On exit    :
 */

static void
_rwNlImageFindFormat(RwImage *image, RwInt32 *format)
{
    RwInt32 x, y;
    RwInt32 width, height, stride, depth;
    RwUInt8 *pixels;
    
    RWFUNCTION(RWSTRING("_rwNlImageFindFormat"));
    RWASSERT(image);

    width  = RwImageGetWidth(image);
    height = RwImageGetHeight(image);
    stride = RwImageGetStride(image);
    depth  = RwImageGetDepth(image);
    pixels = RwImageGetPixels(image);

    *format = 0;

    if ((depth==4) || (depth==8))
    {
        /* Its a 4 or 8 bit image */
        RwRGBA *palette = RwImageGetPalette(image);

        for (y=0; y<height; y++)
        {
            RwUInt8 *currPixel = pixels;

            for (x=0; x<width; x++)
            {
                if (palette[*currPixel].alpha != 0xff)
                {
                    /* has an alpha channel */
                    (*format) |= rwRASTERFORMAT8888;

                    RWRETURNVOID();
                }

                /* Next pixel */

                currPixel++;
            }

            pixels += stride;
        }

        /* Give the appropriate format */
        (*format) |= rwRASTERFORMAT888;
    }
    else if (depth == 32)
    {
        /* We have an 32 bit image */
        for (y=0; y<height; y++)
        {
            RwRGBA *currPixel = (RwRGBA *)pixels;

            for (x=0; x<width; x++)
            {
                if (currPixel->alpha != 0xff)
                {
                    /* has an alpha channel */
                    (*format) |= rwRASTERFORMAT8888;

                    RWRETURNVOID();
                }

                /* Next pixel */

                currPixel++;
            }

            pixels += stride;
        }

        /* Give the appropriate format */
        (*format) |= rwRASTERFORMAT888;
    }

    RWRETURNVOID();
}

/****************************************************************************
 _rwNlImageFindRasterFormat

 On entry   : Raster (OUT)
            : Image
            : Flags
 On exit    : TRUE on success
 */
static          RwBool
_rwNlImageFindRasterFormat(void *pRaster,
                           void *pImage,
                           RwInt32 flags)
{
    RwRaster       *rpRas = (RwRaster *) pRaster;
    RwImage        *ipImage = (RwImage *) pImage;

    RWFUNCTION(RWSTRING("_rwNlImageFindRasterFormat"));

    /* Do the default thing first */
    rpRas->width = ipImage->width;
    rpRas->height = ipImage->height;
    rpRas->depth = 0;          /* Use the default depth for now... */

    /* Try to find the format with what we've got */

    if (!_rwNlGetRasterFormat(NULL,
                              pRaster,
                              flags))
    {
        RWRETURN(FALSE);
    }

    /* The format is already set up */

    /* See whats possible */

    switch (rpRas->cType)
    {
        case rwRASTERTYPETEXTURE:
            {
                RwInt32 format;

                _rwNlImageFindFormat(ipImage, &format);

                /*
                  rpRas->cFormat = (RwUInt8)((format|
                    (flags & (rwRASTERFORMATMIPMAP | 
                    rwRASTERFORMATAUTOMIPMAP)) >> 8));
                */
                rpRas->cFormat = (RwUInt8)((format|
                    (flags & (rwRASTERFORMATMIPMAP | 
                              rwRASTERFORMATAUTOMIPMAP))) >> 8);

                /* We have our format */
                RWRETURN(TRUE);
            }
        case rwRASTERTYPENORMAL:
        case rwRASTERTYPECAMERA:
        case rwRASTERTYPEZBUFFER:
            {
                /* Just take the default case */

                RWRETURN(TRUE);
            }
    }

    RWRETURN(FALSE);
}

/****************************************************************************
 _rwNlRasterCreate

 Create a raster

 On entry   : NULL
            : pRaster - raster to allocate
            : Flags
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterCreate(void *pInOut,
                  void *pRaster,
                  RwInt32 flags)
{
    RwRaster        *rpRas = (RwRaster *) pRaster;
    RwInt32         nFormat = flags & (RwInt32) rwRASTERFORMATMASK;

    RWFUNCTION(RWSTRING("_rwNlRasterCreate"));

    /* Stop warnings */
    pInOut = pInOut;

    if (!_rwNlGetRasterFormat(pInOut,
                              pRaster,
                              flags))
    {
        RWRETURN(FALSE);
    }

    /* Set up */

    rpRas->cpPixels = NULL;

    /* If is not a camera buffer, then we need to allocate real memory for it
     */

    if ((rpRas->width) && (rpRas->height))
    {
        switch (rpRas->cType)
        {
            case rwRASTERTYPETEXTURE:
                {
                    switch (nFormat & rwRASTERFORMATPIXELFORMATMASK)
                    {
                        case rwRASTERFORMAT8888:
                            {
                                rpRas->stride = 4 * rpRas->width;
                                rpRas->cpPixels = 
                                    (RwUInt8 *) RwDriverMalloc(rpRas->stride * rpRas->height);
                                if (!rpRas->cpPixels)
                                {
                                    RWRETURN(FALSE);
                                }
                                break;
                            }
                        case rwRASTERFORMAT888:
                            {
                                rpRas->stride = 3 * rpRas->width;
                                rpRas->cpPixels =
                                    (RwUInt8 *) RwDriverMalloc(rpRas->stride * rpRas->height);
                                if (!rpRas->cpPixels)
                                {
                                    RWRETURN(FALSE);
                                }
                                break;
                            }
                        default:
                            {
                                RWERROR((E_RW_INVRASTERFORMAT));
                                RWRETURN(FALSE);
                            }
                        break;
                    }
                    break;
                }
            case rwRASTERTYPECAMERA:
            case rwRASTERTYPENORMAL:
                {
                    /* Calculate stride */
                    rpRas->stride = 4 * rpRas->width;

                    /* Allocate space */
                    rpRas->cpPixels =
                        (RwUInt8 *) RwDriverMalloc(rpRas->stride * rpRas->height);

                    if (!rpRas->cpPixels)
                    {
                        RWRETURN(FALSE);
                    }
                    break;
                }
            case rwRASTERTYPEZBUFFER:
                {
                    /* Calculate stride */
                    rpRas->stride = 2 * rpRas->width;

                    /* Allocate space */
                    rpRas->cpPixels =
                        (RwUInt8 *) RwDriverMalloc(rpRas->stride * rpRas->height);

                    if (!rpRas->cpPixels)
                    {
                        RWRETURN(FALSE);
                    }
                    break;
                }
            default:
                {
                    RWRETURN(FALSE);
                }
        }
    }
    else
    {
        rpRas->cFlags = (RwUInt8) rwRASTERDONTALLOCATE;  /* Not allocated */
        rpRas->stride = 0;
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterSubRaster

 On entry   : Raster (OUT)
            : Raster (IN)
            : None
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterSubRaster(void *pRas,
                     void *pIn,
                     RwInt32 nIn __RWUNUSED__)
{
    RwRaster       *rpRas = (RwRaster *) pRas;
    RwRaster       *rpIn = (RwRaster *) pIn;
    RwRaster       *pixelOwner = rpIn->parent;

    RWFUNCTION(RWSTRING("_rwNlRasterSubRaster"));

    /* Core already set up offset and size */

    /* This baby has most certainly not been allocated */
    rpRas->cFormat = rpIn->cFormat;
    rpRas->stride = rpIn->stride;
    rpRas->depth = rpIn->depth;
    rpRas->cType = rpIn->cType;

    switch(rpIn->depth)
    {
        case 24:
        {
            rpRas->cpPixels = (pixelOwner->cpPixels) +
                (pixelOwner->stride * rpRas->nOffsetY) +
                (rpRas->nOffsetX * 3);
            break;
        }
        case 32:
        {
            rpRas->cpPixels = (pixelOwner->cpPixels) +
                (pixelOwner->stride * rpRas->nOffsetY) +
                (rpRas->nOffsetX * 4);
            break;
        }
        default:
        {
            RWERROR((E_RW_INVRASTERFORMAT));
            RWRETURN(FALSE);
        }
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterDestroy

 On entry   : NULL
            : Raster to destroy
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterDestroy(void *pOut,
                   void *pRaster,
                   RwInt32 nIn)
{
    RwRaster       *rpRas = (RwRaster *) pRaster;

    RWFUNCTION(RWSTRING("_rwNlRasterDestroy"));

    /* Stop warnings */
    pOut = pOut;
    nIn = nIn;

    if (!(rpRas->cFlags & (RwUInt8) rwRASTERDONTALLOCATE))
    {
        RwDriverFree(rpRas->cpPixels);
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterLock

 On entry   : Pixel pointer
            : raster
            : Flags
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterLock(void *pPixels,
                void *pRaster,
                RwInt32 flags)
{
    RwUInt8     **cppPixels = (RwUInt8 **)pPixels;
    RwRaster    *raster = (RwRaster *)pRaster;
    RwUInt8     mipLevel = (RwUInt8)(((RwInt32)0xFF00 & flags) >> 8);

    RWFUNCTION(RWSTRING("_rwNlRasterLock"));

    flags = flags & 0xFF;

    if (mipLevel > 0)
    {
        RWRETURN(FALSE);
    }

    (*cppPixels) = raster->cpPixels;

    if (flags & rwRASTERLOCKREAD)
    {
        raster->privateFlags |= rwRASTERPIXELLOCKEDREAD;
    }
    if (flags & rwRASTERLOCKWRITE)
    {
        raster->privateFlags |= rwRASTERPIXELLOCKEDWRITE;
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterUnlock

 On entry   : NULL
            : Rastre
            : Flags
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterUnlock(void *pInOut,
                  void *pRaster,
                  RwInt32 flags)
{
    RwRaster    *raster = (RwRaster *)pRaster;

    RWFUNCTION(RWSTRING("_rwNlRasterUnlock"));

    /* Stop warnings */
    pInOut = pInOut;
    pRaster = pRaster;
    flags = flags;

    raster->privateFlags &= ~rwRASTERPIXELLOCKED;

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterGetMipLevels

 On entry   : Pixel pointer
            : raster
            : Flags
 On exit    : TRUE on success
 */
static RwBool
_rwNlRasterGetMipLevels(void *pOut,
                        void *pInOut,
                        RwInt32 nI __RWUNUSED__)
{
    RwRaster    *raster = (RwRaster *)pInOut;
    RwInt32     *numMipLevels = (RwInt32 *)pOut;

    RWFUNCTION(RWSTRING("_rwNlRasterGetMipLevels"));
    RWASSERT(raster);
    RWASSERT(numMipLevels);

    if (raster->cpPixels)
    {
        *numMipLevels = 1;

    }
    else
    {
        *numMipLevels = 0;
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterClear

 On entry   : NULL
            : NULL
            : Pixel value defining what to clear the raster to
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterClear(void *pOut __RWUNUSED__,
                 void *pInOut __RWUNUSED__,
                 RwInt32 nValue __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlRasterClear"));

    /* Do nothing here */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterClearRect

 On entry   : NULL
            : RwRect defining area to clear
            : Pixel value defining what to clear the raster to
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterClearRect(void *pOut __RWUNUSED__,
                     void *pRect __RWUNUSED__,
                     RwInt32 nAvlue __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlRasterClearRect"));

    /* Do nothing */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterRender

 On entry   : Raster to render (blit)
            : RwRect defining location to blit to (take x and y only)
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterRender(void *pRas __RWUNUSED__,
                  void *pLoc __RWUNUSED__,
                  RwInt32 nData __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlRasterRender"));

    /* Do nothing */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterRenderScaled

 On entry   : Raster to render (blit)
            : RwRect defining region to blit to (take x,
 y,
 w and h)
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterRenderScaled(void *pRas __RWUNUSED__,
                        void *pRect __RWUNUSED__,
                        RwInt32 nData __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlRasterRenderScaled"));

    /* Do nothing */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterRenderFast

 On entry   : Raster to render (blit) - raster has no mask info, so optimise for this
            : RwRect defining location to blit to (take x and y only)
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterRenderFast(void *pRas __RWUNUSED__,
                      void *pLoc __RWUNUSED__,
                      RwInt32 nData __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlRasterRenderFast"));

    /* Do nothing */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlRasterSetContext

 On entry   : NULL
            : Raster to be the destination of future raster ops.
            : 0
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterSetContext(void *pOut __RWUNUSED__,
                      void *pRas __RWUNUSED__,
                      RwInt32 nData __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlRasterSetContext"));

    /* Do nothing */
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwNlImageGetRaster

 On entry   : Image (MODIFY)
            : Raster
 On exit    : TRUE on success
 */
static          RwBool
_rwNlImageGetRaster(void *pImage,
                    void *pRaster,
                    RwInt32 nNum __RWUNUSED__)
{
    RwRaster       *raster = (RwRaster *) pRaster;
    RwImage        *image = (RwImage *) pImage;
    RwBool          alreadyPixelLocked = FALSE;
    RwBool          result = TRUE;

    RWFUNCTION(RWSTRING("_rwNlImageGetRaster"));

    if (image->cpPixels)
    {
        RwInt32         x;
        RwInt32         y;
        RwUInt8         *srcPixels = NULL;
        RwRasterFormat  format;

        if ((image->width < raster->width) ||
            (image->height < raster->height) ||
            (image->depth != 32))
        {
            RWRETURN(FALSE);
        }

        if (raster->privateFlags & rwRASTERPIXELLOCKEDREAD)
        {
            alreadyPixelLocked = TRUE;
        }
        else
        {
            srcPixels = RwRasterLock(raster, 0, rwRASTERLOCKREAD);
            if (srcPixels == NULL)
            {
                RWRETURN(FALSE);
            }
        }

        format = (RwRasterFormat) RwRasterGetFormat(raster);
        switch (format & rwRASTERFORMATPIXELFORMATMASK)
        {
            case rwRASTERFORMAT888:
            {
                RwUInt8     *rasterPixels;
                RwUInt8     *dstPixels;
                RwRGBA      *imageRGBA;

                dstPixels = image->cpPixels;

                for (y = 0; y < raster->height; y++)
                {
                    rasterPixels = srcPixels;
                    imageRGBA = (RwRGBA *)dstPixels;
                    for (x = 0; x < raster->width; x++)
                    {
                        imageRGBA->red = *(rasterPixels++);
                        imageRGBA->green = *(rasterPixels++);
                        imageRGBA->blue = *(rasterPixels++);
                        imageRGBA->alpha = 0xFF;

                        imageRGBA++;
                    }
                    srcPixels += raster->stride;
                    dstPixels += image->stride;
                }
                break;
            }
            case rwRASTERFORMAT8888:
            {
                RwRGBA      *rasterRGBA;
                RwUInt8     *dstPixels;
                RwRGBA      *imageRGBA;

                dstPixels = image->cpPixels;

                for (y = 0; y < raster->height; y++)
                {
                    rasterRGBA = (RwRGBA *)srcPixels;
                    imageRGBA = (RwRGBA *)dstPixels;
                    for (x = 0; x < raster->width; x++)
                    {
                        imageRGBA->red = rasterRGBA->red;
                        imageRGBA->green = rasterRGBA->green;
                        imageRGBA->blue = rasterRGBA->blue;
                        imageRGBA->alpha = rasterRGBA->alpha;

                        imageRGBA++;
                        rasterRGBA++;
                    }
                    srcPixels += raster->stride;
                    dstPixels += raster->stride;
                }
                break;
            }
            default:
            {
                RWERROR((E_RW_INVRASTERFORMAT));
                result = FALSE;
            }
        }

        if (!alreadyPixelLocked)
        {
            RwRasterUnlock(raster);
        }
        RWRETURN(result);
    }

    RWERROR((E_RW_INVIMAGEFORMAT));
    RWRETURN(FALSE);
}

/****************************************************************************
 _rwNlRasterSetImage

 The raster is only set if it has a valid pixel pointer

 On entry   : Raster (MODIFY)
            : Image
            : Flags
 On exit    : TRUE on success
 */
static          RwBool
_rwNlRasterSetImage(void *pRaster,
                    void *pImage,
                    RwInt32 flags)
{
    RwRaster        *rpRas = (RwRaster *) pRaster;
    RwImage         *ipImage = (RwImage *) pImage;
    RwInt32         nI, nJ;
    RwUInt8         *cpOut = (RwUInt8 *) rpRas->cpPixels;
    RwUInt8         *cpIn = (RwUInt8 *) ipImage->cpPixels;
    RwBool          alreadyPixelLocked = FALSE;
    RwBool          result = TRUE;
    RwRasterFormat  nFormat = (RwRasterFormat) RwRasterGetFormat(rpRas);

    RWFUNCTION(RWSTRING("_rwNlRasterSetImage"));

    /* Stop warnings */
    flags = flags;

    if (rpRas->privateFlags & rwRASTERPIXELLOCKEDWRITE)
    {
        alreadyPixelLocked = TRUE;
    }
    else
    {
        if (!RwRasterLock(rpRas, 0, rwRASTERLOCKWRITE))
        {
            RWRETURN(FALSE);
        }
    }

    switch (ipImage->depth)
    {
        case 4:
        case 8:
        {
            RwRGBA         *rpPal = ipImage->palette;

            /* Its a 4 or 8 bit image */

            switch (nFormat & rwRASTERFORMATPIXELFORMATMASK)
            {
                case rwRASTERFORMAT888:
                {
                    for (nI = 0; nI < rpRas->height; nI++)
                    {
                        RwUInt8     *npOut = (RwUInt8 *) cpOut;

                        for (nJ = 0; nJ < rpRas->width; nJ++)
                        {
                            RwRGBA      *rpIn;

                            rpIn = &rpPal[cpIn[nJ]];
                            *(npOut++) = rpIn->red;
                            *(npOut++) = rpIn->green;
                            *(npOut++) = rpIn->blue;
                        }
                        cpOut += rpRas->stride;
                        cpIn += ipImage->stride;
                    }
                    break;
                }
                case rwRASTERFORMAT8888:
                {
                    for (nI = 0; nI < rpRas->height; nI++)
                    {
                        RwRGBA       *npOut = (RwRGBA *) cpOut;

                        for (nJ = 0; nJ < rpRas->width; nJ++)
                        {
                            RwRGBA         *rpIn;

                            rpIn = &rpPal[cpIn[nJ]];

                            /*(*npOut) =
                                (((RwUInt32) rpIn->red) << 24) |
                                (((RwUInt32) rpIn->green) << 16) |
                                (((RwUInt32) rpIn->blue) << 8) |
                                (((RwUInt32) rpIn->alpha) << 0);*/
                            *npOut = *rpIn;
                            
                            /* Next pixel */

                            npOut++;
                        }

                        cpOut += rpRas->stride;
                        cpIn += ipImage->stride;
                    }

                    break;
                }
                default:
                {
                    RWERROR((E_RW_INVRASTERFORMAT));
                    result = FALSE;
                }
            }
            break;
        }
        case 32:
        {
            /* Its a 32 bit image */
            switch (nFormat & rwRASTERFORMATPIXELFORMATMASK)
            {
                case rwRASTERFORMAT888:
                {
                    for (nI = 0; nI < rpRas->height; nI++)
                    {
                        RwUInt8     *npOut = (RwUInt8 *) cpOut;
                        RwRGBA         *rpIn = (RwRGBA *) cpIn;

                        for (nJ = 0; nJ < rpRas->width; nJ++)
                        {
                            *(npOut++) = rpIn->red;
                            *(npOut++) = rpIn->green;
                            *(npOut++) = rpIn->blue;

                            rpIn++;
                        }
                        cpOut += rpRas->stride;
                        cpIn += ipImage->stride;
                    }
                    break;
                }
                case rwRASTERFORMAT8888:
                {

                    for (nI = 0; nI < rpRas->height; nI++)
                    {
                        RwRGBA          *npOut = (RwRGBA *) cpOut;
                        RwRGBA          *rpIn = (RwRGBA *) cpIn;

                        for (nJ = 0; nJ < rpRas->width; nJ++)
                        {
                            /*(*npOut) =
                                (((RwUInt32) rpIn->red) << 24) |
                                (((RwUInt32) rpIn->green) << 16) |
                                (((RwUInt32) rpIn->blue) << 8) |
                                (((RwUInt32) rpIn->alpha) << 0);*/
                            *npOut = *rpIn;

                            /* Next pixel */

                            npOut++;
                            rpIn++;
                        }

                        cpOut += rpRas->stride;
                        cpIn += ipImage->stride;
                    }

                    break;
                }
                default:
                {
                    RWERROR((E_RW_INVRASTERFORMAT));
                    result = FALSE;
                }
            }
            break;
        }
        default:
        {
            RWERROR((E_RW_INVIMAGEDEPTH));
            result = FALSE;
        }
    }

    if (!alreadyPixelLocked)
    {
        RwRasterUnlock(rpRas);
    }
    RWRETURN(result);
}

static          RwBool
_rwNlHintRenderFront2Back(void *pInOut __RWUNUSED__,
                          void *pIn __RWUNUSED__,
                          RwInt32 flags __RWUNUSED__)
{
    RWFUNCTION(RWSTRING("_rwNlHintRenderFront2Back"));
    RWRETURN(TRUE);
}

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

                              Default

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

/******************* Standard functions ************************************/

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

                      Setting up of standard functions

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

/****************************************************************************
 _rwNlNoStandard

 On entry   : pOut
            : pInOut
            : nIn
 On exit    : FALSE
 */
static          RwBool
_rwNlNoStandard(void *pOut,
                void *pInOut,
                RwInt32 nIn)
{
    RWFUNCTION(RWSTRING("_rwNlNoStandard"));

    /* Stop warnings */
    pOut = pOut;
    pInOut = pInOut;
    nIn = nIn;

    RWRETURN(FALSE);
}

/****************************************************************************
 _rwNlSetStandards

 On entry   : Standard functions
            : Space
            : Standards
            : Amount of standards
 On exit    :
 */
static void
_rwNlSetStandards(RwStandardFunc * fppFuncs,
                  RwInt32 nNumFuncs,
                  RwStandard * spStandards,
                  RwInt32 nNumStandards)
{
    RwInt32         nI;

    RWFUNCTION(RWSTRING("_rwNlSetStandards"));

    /* Clear out all of the standards initially */
    for (nI = 0; nI < nNumFuncs; nI++)
    {
        fppFuncs[nI] = _rwNlNoStandard;
    }

    /* Fill in all of the standards */

    while (nNumStandards--)
    {
        if ((spStandards->nStandard < nNumFuncs) &&
            (spStandards->nStandard >= 0))
        {
            fppFuncs[spStandards->nStandard] =
                spStandards->fpStandard;
        }

        spStandards++;
    }

    RWRETURNVOID();
}

static RwBool
devicesystemrxpipelinerequestpipe(void *pOut,
                                 void *pInOut __RWUNUSED__, 
                                 RwInt32 nIn __RWUNUSED__)
{
    RxPipeline       **pipelineRef = (RxPipeline **) pOut;

    RWFUNCTION(RWSTRING("devicesystemrxpipelinerequestpipe"));

    *pipelineRef = NULL;

    RWRETURN(TRUE);
}


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

                           System access

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

/****************************************************************************
 _rwNlSystem

 On entry   : Request
            : Data out
            : Data in/out
            : Number in
 On exit    : TRUE on success
 */
static          RwBool
_rwNlSystem(RwInt32 nRequest,
            void *pOut,
            void *pInOut,
            RwInt32 nIn)
{
    RWFUNCTION(RWSTRING("_rwNlSystem"));

    switch (nRequest)
    {

        /******************* Opening/Closing **********************/

            /* Gives the device access to the libraries device block */

        case rwDEVICESYSTEMREGISTER:
            {
                RwDevice       *dpDevice = (RwDevice *) pOut;
                RwMemoryFunctions *mfpFuncs = (RwMemoryFunctions *) pInOut;
                RwDevice       *lpdGD3d = _rwDeviceGetHandle();

                /* Set up the fast device access.. */

                *dpDevice = *lpdGD3d;

                dgGGlobals.memFuncs = mfpFuncs;

                RWRETURN(TRUE);
            }

        /******************* Opening/Closing **********************/

            /* The purpose of Open is to put the hardware in a state where
             * it can be queried (about its capabilities). This function also
             * tests if it is a candidate at all (like it can be started at
             * all). NOTE this includes setting up a minimal set of standard
             * handlers that can be used for querying the device. */

        case rwDEVICESYSTEMOPEN:
            {
                /* We're not interested in the window handle here, but if we
                 * were, it would be accessible as:
                 * (HWND)(((RwEngineOpenParams *)pInOut)->displayID);
                 */

                /* Done */
                RWRETURN(TRUE);
            }

            /* The purpose of Close is to remove any set up that was performed
             * by Open */

        case rwDEVICESYSTEMCLOSE:
            {
                /* All done - nothing to do */
                RWRETURN(TRUE);
            }

        /************** Starting stopping ******************/

            /* Start means that this bit of hardware has been selected for
             * rendering - so get it into a state ready for rendering */

        case rwDEVICESYSTEMSTART:
            {
                /* All done */
                RWRETURN(TRUE);
            }

            /* Puts back the hardware into the state it was in immediately
             * after being opened */

        case rwDEVICESYSTEMSTOP:
            {

                /* All done */
                RWRETURN(TRUE);
            }

            /************* pipeline initialisation **************************/

        case rwDEVICESYSTEMINITPIPELINE:
            {
                RWRETURN(TRUE);
            }
            

        case rwDEVICESYSTEMRXPIPELINEREQUESTPIPE:
            {
                RwBool result;

                result =
                    devicesystemrxpipelinerequestpipe(pOut, pInOut, nIn);
                RWRETURN(result);
            }

        /************* standard device functions ************************/

        case rwDEVICESYSTEMSTANDARDS:
            {
                /* Standard functions */
                static RwStandard saGStandards[] =
                {
                /* Camera ops */
                    {rwSTANDARDCAMERABEGINUPDATE, _rwNlCameraBeginUpdate},
                    {rwSTANDARDCAMERAENDUPDATE, _rwNlCameraEndUpdate},
                    {rwSTANDARDCAMERACLEAR, _rwNlCameraClear},

                /* Raster/Pixel operations */
                    {rwSTANDARDRASTERSHOWRASTER, _rwNlRasterShowRaster},
                    {rwSTANDARDRGBTOPIXEL, _rwNlRGBToPixel},
                    {rwSTANDARDPIXELTORGB, _rwNlPixelToRGB},
                    {rwSTANDARDRASTERSETIMAGE, _rwNlRasterSetImage},
                    {rwSTANDARDIMAGEGETRASTER, _rwNlImageGetRaster},

                /* Raster creation and destruction */
                    {rwSTANDARDRASTERDESTROY, _rwNlRasterDestroy},
                    {rwSTANDARDRASTERCREATE, _rwNlRasterCreate},
                    {rwSTANDARDRASTERSUBRASTER, _rwNlRasterSubRaster},

                /* Finding about a raster type */
                    {rwSTANDARDIMAGEFINDRASTERFORMAT, _rwNlImageFindRasterFormat},

                /* Texture operations */
                    {rwSTANDARDTEXTURESETRASTER, _rwNlTextureSetRaster},

                /* Locking and releasing */
                    {rwSTANDARDRASTERLOCK, _rwNlRasterLock},
                    {rwSTANDARDRASTERUNLOCK, _rwNlRasterUnlock},

                /* Raster operations */
                    {rwSTANDARDRASTERCLEAR, _rwNlRasterClear},
                    {rwSTANDARDRASTERCLEARRECT, _rwNlRasterClearRect},
                    {rwSTANDARDRASTERRENDER, _rwNlRasterRender},
                    {rwSTANDARDRASTERRENDERSCALED, _rwNlRasterRenderScaled},
                    {rwSTANDARDRASTERRENDERFAST, _rwNlRasterRenderFast},
                    {rwSTANDARDSETRASTERCONTEXT, _rwNlRasterSetContext},

                /* Render order hint */
                    {rwSTANDARDHINTRENDERF2B, _rwNlHintRenderFront2Back},

                /* Raster mip levels */
                    {rwSTANDARDRASTERGETMIPLEVELS, _rwNlRasterGetMipLevels}
                };

                _rwNlSetStandards((RwStandardFunc *) pOut, nIn,
                                  saGStandards,
                                  sizeof (saGStandards) / sizeof (RwStandard));

                RWRETURN(TRUE);
            }

        /******************* not supported ******************************/

        default:
            {
                break;
            }
    }

    RWRETURN(FALSE);
}

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

                     Getting the device structure

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

/****************************************************************************
 _rwDeviceGetHandle

 On entry   :
 On exit    : device block handle
 */
RwDevice       *
_rwDeviceGetHandle(void)
{
    static RwDevice dGNullDevice =
    {
        (RwReal) (1),  /* Default gamma correction */

        _rwNlSystem,

        (RwReal) (65535.0), (RwReal) (0.0),  /* Z buffer limits - what does it matter? */

        _rwNlSetRenderState,  /* Setting the rendering state */
        _rwNlGetRenderState,  /* Getting the rendering state */

        _rwNlIm2DRenderLine,  /* Line rendering function */
        _rwNlIm2DRenderTriangle,  /* Rendering a triangle under the current state */
        _rwNlIm2DRenderPrimitive,
        _rwNlIm2DRenderIndexedPrimitive,

    /* These get set up when the immediate mode module is set up */
        NULL, NULL, NULL, NULL
    };         /* NULL system */

    RWFUNCTION(RWSTRING("_rwDeviceGetHandle"));

    RWRETURN(&dGNullDevice);
}
