/* Copyright (c) 2001, Stanford University
 * All rights reserved
 *
 * See the file LICENSE.txt for information on redistributing this software.
 */

#include "state.h"
#include "state/cr_statetypes.h"
#include "state/cr_texture.h"
#include "cr_hash.h"
#include "cr_string.h"
#include "cr_mem.h"
#include "cr_version.h"
#include "state_internals.h"

#ifdef DEBUG_misha
#include <iprt/assert.h>
#endif

#define UNUSED(x) ((void) (x))

#define GET_TOBJ(tobj, state, id) \
  tobj = (CRTextureObj *) crHashtableSearch(g->shared->textureTable, id);


void crStateTextureDestroy(CRContext *ctx)
{
    crStateDeleteTextureObjectData(&ctx->texture.base1D);
    crStateDeleteTextureObjectData(&ctx->texture.proxy1D);
    crStateDeleteTextureObjectData(&ctx->texture.base2D);
    crStateDeleteTextureObjectData(&ctx->texture.proxy2D);
#ifdef CR_OPENGL_VERSION_1_2
    crStateDeleteTextureObjectData(&ctx->texture.base3D);
    crStateDeleteTextureObjectData(&ctx->texture.proxy3D);
#endif
#ifdef CR_ARB_texture_cube_map
    crStateDeleteTextureObjectData(&ctx->texture.baseCubeMap);
    crStateDeleteTextureObjectData(&ctx->texture.proxyCubeMap);
#endif
#ifdef CR_NV_texture_rectangle
    crStateDeleteTextureObjectData(&ctx->texture.baseRect);
    crStateDeleteTextureObjectData(&ctx->texture.proxyRect);
#endif
}


void crStateTextureInit(CRContext *ctx)
{
    CRLimitsState *limits = &ctx->limits;
    CRTextureState *t = &ctx->texture;
    CRStateBits *sb = GetCurrentBits();
    CRTextureBits *tb = &(sb->texture);
    unsigned int i;
    unsigned int a;
    GLvectorf zero_vector = {0.0f, 0.0f, 0.0f, 0.0f};
    GLcolorf zero_color = {0.0f, 0.0f, 0.0f, 0.0f};
    GLvectorf x_vector = {1.0f, 0.0f, 0.0f, 0.0f};
    GLvectorf y_vector = {0.0f, 1.0f, 0.0f, 0.0f};

    /* compute max levels from max sizes */
    for (i=0, a=limits->maxTextureSize; a; i++, a=a>>1);
    t->maxLevel = i-1;
    for (i=0, a=limits->max3DTextureSize; a; i++, a=a>>1);
    t->max3DLevel = i-1;
#ifdef CR_ARB_texture_cube_map
    for (i=0, a=limits->maxCubeMapTextureSize; a; i++, a=a>>1);
    t->maxCubeMapLevel = i-1;
#endif
#ifdef CR_NV_texture_rectangle
    for (i=0, a=limits->maxRectTextureSize; a; i++, a=a>>1);
    t->maxRectLevel = i-1;
#endif

    crStateTextureInitTextureObj(ctx, &(t->base1D), 0, GL_TEXTURE_1D);
    crStateTextureInitTextureObj(ctx, &(t->base2D), 0, GL_TEXTURE_2D);
#ifdef CR_OPENGL_VERSION_1_2
    crStateTextureInitTextureObj(ctx, &(t->base3D), 0, GL_TEXTURE_3D);
#endif
#ifdef CR_ARB_texture_cube_map
    crStateTextureInitTextureObj(ctx, &(t->baseCubeMap), 0,
        GL_TEXTURE_CUBE_MAP_ARB);
#endif
#ifdef CR_NV_texture_rectangle
    crStateTextureInitTextureObj(ctx, &(t->baseRect), 0,
        GL_TEXTURE_RECTANGLE_NV);
#endif

    crStateTextureInitTextureObj(ctx, &(t->proxy1D), 0, GL_TEXTURE_1D);
    crStateTextureInitTextureObj(ctx, &(t->proxy2D), 0, GL_TEXTURE_2D);
#ifdef CR_OPENGL_VERSION_1_2
    crStateTextureInitTextureObj(ctx, &(t->proxy3D), 0, GL_TEXTURE_3D);
#endif
#ifdef CR_ARB_texture_cube_map
    crStateTextureInitTextureObj(ctx, &(t->proxyCubeMap), 0,
        GL_TEXTURE_CUBE_MAP_ARB);
#endif
#ifdef CR_NV_texture_rectangle
    crStateTextureInitTextureObj(ctx, &(t->proxyRect), 0,
        GL_TEXTURE_RECTANGLE_NV);
#endif

    t->curTextureUnit = 0;

    /* Per-unit initialization */
    for (i = 0; i < limits->maxTextureUnits; i++)
    {
        t->unit[i].currentTexture1D = &(t->base1D);
        t->unit[i].currentTexture2D = &(t->base2D);
        t->unit[i].currentTexture3D = &(t->base3D);
#ifdef CR_ARB_texture_cube_map
        t->unit[i].currentTextureCubeMap = &(t->baseCubeMap);
#endif
#ifdef CR_NV_texture_rectangle
        t->unit[i].currentTextureRect = &(t->baseRect);
#endif

        t->unit[i].enabled1D = GL_FALSE;
        t->unit[i].enabled2D = GL_FALSE;
        t->unit[i].enabled3D = GL_FALSE;
        t->unit[i].enabledCubeMap = GL_FALSE;
#ifdef CR_NV_texture_rectangle
        t->unit[i].enabledRect = GL_FALSE;
#endif
        t->unit[i].textureGen.s = GL_FALSE;
        t->unit[i].textureGen.t = GL_FALSE;
        t->unit[i].textureGen.r = GL_FALSE;
        t->unit[i].textureGen.q = GL_FALSE;

        t->unit[i].gen.s = GL_EYE_LINEAR;
        t->unit[i].gen.t = GL_EYE_LINEAR;
        t->unit[i].gen.r = GL_EYE_LINEAR;
        t->unit[i].gen.q = GL_EYE_LINEAR;

        t->unit[i].objSCoeff = x_vector;
        t->unit[i].objTCoeff = y_vector;
        t->unit[i].objRCoeff = zero_vector;
        t->unit[i].objQCoeff = zero_vector;

        t->unit[i].eyeSCoeff = x_vector;
        t->unit[i].eyeTCoeff = y_vector;
        t->unit[i].eyeRCoeff = zero_vector;
        t->unit[i].eyeQCoeff = zero_vector;
        t->unit[i].envMode = GL_MODULATE;
        t->unit[i].envColor = zero_color;

        t->unit[i].combineModeRGB = GL_MODULATE;
        t->unit[i].combineModeA = GL_MODULATE;
        t->unit[i].combineSourceRGB[0] = GL_TEXTURE;
        t->unit[i].combineSourceRGB[1] = GL_PREVIOUS_EXT;
        t->unit[i].combineSourceRGB[2] = GL_CONSTANT_EXT;
        t->unit[i].combineSourceA[0] = GL_TEXTURE;
        t->unit[i].combineSourceA[1] = GL_PREVIOUS_EXT;
        t->unit[i].combineSourceA[2] = GL_CONSTANT_EXT;
        t->unit[i].combineOperandRGB[0] = GL_SRC_COLOR;
        t->unit[i].combineOperandRGB[1] = GL_SRC_COLOR;
        t->unit[i].combineOperandRGB[2] = GL_SRC_ALPHA;
        t->unit[i].combineOperandA[0] = GL_SRC_ALPHA;
        t->unit[i].combineOperandA[1] = GL_SRC_ALPHA;
        t->unit[i].combineOperandA[2] = GL_SRC_ALPHA;
        t->unit[i].combineScaleRGB = 1.0F;
        t->unit[i].combineScaleA = 1.0F;
#ifdef CR_EXT_texture_lod_bias
        t->unit[i].lodBias = 0.0F;
#endif
        RESET(tb->enable[i], ctx->bitid);
        RESET(tb->current[i], ctx->bitid);
        RESET(tb->objGen[i], ctx->bitid);
        RESET(tb->eyeGen[i], ctx->bitid);
        RESET(tb->genMode[i], ctx->bitid);
        RESET(tb->envBit[i], ctx->bitid);
    }
    RESET(tb->dirty, ctx->bitid);
}


void
crStateTextureInitTextureObj(CRContext *ctx, CRTextureObj *tobj,
                             GLuint name, GLenum target)
{
    const CRTextureState *t = &(ctx->texture);
    int i, face;

    tobj->borderColor.r = 0.0f;
    tobj->borderColor.g = 0.0f;
    tobj->borderColor.b = 0.0f;
    tobj->borderColor.a = 0.0f;
    tobj->minFilter     = GL_NEAREST_MIPMAP_LINEAR;
    tobj->magFilter     = GL_LINEAR;
    tobj->wrapS         = GL_REPEAT;
    tobj->wrapT         = GL_REPEAT;
#ifdef CR_OPENGL_VERSION_1_2
    tobj->wrapR         = GL_REPEAT;
    tobj->priority      = 1.0f;
    tobj->minLod        = -1000.0;
    tobj->maxLod        = 1000.0;
    tobj->baseLevel     = 0;
    tobj->maxLevel      = 1000;
#endif
    tobj->target        = target;
    tobj->id            = name;
    tobj->hwid          = 0;

#ifndef IN_GUEST
    crStateGetTextureObjHWID(tobj);
#endif

    CRASSERT(t->maxLevel);

    /* XXX don't always need all six faces */
    for (face = 0; face < 6; face++) {
        /* allocate array of mipmap levels */
        CRASSERT(t->maxLevel < CR_MAX_MIPMAP_LEVELS);
        tobj->level[face] = (CRTextureLevel *)
            crCalloc(sizeof(CRTextureLevel) * CR_MAX_MIPMAP_LEVELS);

        if (!tobj->level[face])
            return; /* out of memory */

        /* init non-zero fields */
        for (i = 0; i <= t->maxLevel; i++) {
            CRTextureLevel *tl = &(tobj->level[face][i]);
            tl->internalFormat = GL_ONE;
            tl->format = GL_RGBA;
            tl->type = GL_UNSIGNED_BYTE;
            crStateTextureInitTextureFormat( tl, tl->internalFormat );
        }
    }

#ifdef CR_EXT_texture_filter_anisotropic
    tobj->maxAnisotropy = 1.0f;
#endif

#ifdef CR_ARB_depth_texture
    tobj->depthMode = GL_LUMINANCE;
#endif

#ifdef CR_ARB_shadow
    tobj->compareMode = GL_NONE;
    tobj->compareFunc = GL_LEQUAL;
#endif

#ifdef CR_ARB_shadow_ambient
    tobj->compareFailValue = 0.0;
#endif

    RESET(tobj->dirty, ctx->bitid);
    RESET(tobj->imageBit, ctx->bitid);
    for (i = 0; i < CR_MAX_TEXTURE_UNITS; i++)
    {
        RESET(tobj->paramsBit[i], ctx->bitid);
    }
}


/* ================================================================
 * Texture internal formats:
 */

const CRTextureFormat _texformat_rgba8888 = {
   8,               /* RedBits */
   8,               /* GreenBits */
   8,               /* BlueBits */
   8,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_argb8888 = {
   8,               /* RedBits */
   8,               /* GreenBits */
   8,               /* BlueBits */
   8,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_rgb888 = {
   8,               /* RedBits */
   8,               /* GreenBits */
   8,               /* BlueBits */
   0,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_rgb565 = {
   5,               /* RedBits */
   6,               /* GreenBits */
   5,               /* BlueBits */
   0,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_argb4444 = {
   4,               /* RedBits */
   4,               /* GreenBits */
   4,               /* BlueBits */
   4,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_argb1555 = {
   5,               /* RedBits */
   5,               /* GreenBits */
   5,               /* BlueBits */
   1,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_al88 = {
   0,               /* RedBits */
   0,               /* GreenBits */
   0,               /* BlueBits */
   8,               /* AlphaBits */
   8,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_rgb332 = {
   3,               /* RedBits */
   3,               /* GreenBits */
   2,               /* BlueBits */
   0,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_a8 = {
   0,               /* RedBits */
   0,               /* GreenBits */
   0,               /* BlueBits */
   8,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_l8 = {
   0,               /* RedBits */
   0,               /* GreenBits */
   0,               /* BlueBits */
   0,               /* AlphaBits */
   8,               /* LuminanceBits */
   0,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_i8 = {
   0,               /* RedBits */
   0,               /* GreenBits */
   0,               /* BlueBits */
   0,               /* AlphaBits */
   0,               /* LuminanceBits */
   8,               /* IntensityBits */
   0,               /* IndexBits */
};

const CRTextureFormat _texformat_ci8 = {
   0,               /* RedBits */
   0,               /* GreenBits */
   0,               /* BlueBits */
   0,               /* AlphaBits */
   0,               /* LuminanceBits */
   0,               /* IntensityBits */
   8,               /* IndexBits */
};


/**
 * Given an internal texture format enum or 1, 2, 3, 4 initialize the
 * texture levels texture format.  This basically just indicates the
 * number of red, green, blue, alpha, luminance, etc. bits are used to
 * store the image.
 */
void
crStateTextureInitTextureFormat( CRTextureLevel *tl, GLenum internalFormat )
{
    switch (internalFormat) {
    case 4:
    case GL_RGBA:
    case GL_COMPRESSED_RGBA_ARB:
#ifdef CR_EXT_texture_sRGB
    case GL_SRGB_ALPHA_EXT:
    case GL_SRGB8_ALPHA8_EXT:
    case GL_COMPRESSED_SRGB_ALPHA_EXT:
#endif
        tl->texFormat = &_texformat_rgba8888;
        break;

    case 3:
    case GL_RGB:
    case GL_COMPRESSED_RGB_ARB:
#ifdef CR_EXT_texture_sRGB
    case GL_SRGB_EXT:
    case GL_SRGB8_EXT:
    case GL_COMPRESSED_SRGB_EXT:
#endif
        tl->texFormat = &_texformat_rgb888;
        break;

    case GL_RGBA2:
    case GL_RGBA4:
    case GL_RGB5_A1:
    case GL_RGBA8:
    case GL_RGB10_A2:
    case GL_RGBA12:
    case GL_RGBA16:
        tl->texFormat = &_texformat_rgba8888;
        break;

    case GL_R3_G3_B2:
        tl->texFormat = &_texformat_rgb332;
        break;
    case GL_RGB4:
    case GL_RGB5:
    case GL_RGB8:
    case GL_RGB10:
    case GL_RGB12:
    case GL_RGB16:
        tl->texFormat = &_texformat_rgb888;
        break;

    case GL_ALPHA:
    case GL_ALPHA4:
    case GL_ALPHA8:
    case GL_ALPHA12:
    case GL_ALPHA16:
    case GL_COMPRESSED_ALPHA_ARB:
        tl->texFormat = &_texformat_a8;
        break;

    case 1:
    case GL_LUMINANCE:
    case GL_LUMINANCE4:
    case GL_LUMINANCE8:
    case GL_LUMINANCE12:
    case GL_LUMINANCE16:
    case GL_COMPRESSED_LUMINANCE_ARB:
#ifdef CR_EXT_texture_sRGB
    case GL_SLUMINANCE_EXT:
    case GL_SLUMINANCE8_EXT:
    case GL_COMPRESSED_SLUMINANCE_EXT:
#endif
        tl->texFormat = &_texformat_l8;
        break;

    case 2:
    case GL_LUMINANCE_ALPHA:
    case GL_LUMINANCE4_ALPHA4:
    case GL_LUMINANCE6_ALPHA2:
    case GL_LUMINANCE8_ALPHA8:
    case GL_LUMINANCE12_ALPHA4:
    case GL_LUMINANCE12_ALPHA12:
    case GL_LUMINANCE16_ALPHA16:
    case GL_COMPRESSED_LUMINANCE_ALPHA_ARB:
#ifdef CR_EXT_texture_sRGB
    case GL_SLUMINANCE_ALPHA_EXT:
    case GL_SLUMINANCE8_ALPHA8_EXT:
    case GL_COMPRESSED_SLUMINANCE_ALPHA_EXT:
#endif
        tl->texFormat = &_texformat_al88;
        break;

    case GL_INTENSITY:
    case GL_INTENSITY4:
    case GL_INTENSITY8:
    case GL_INTENSITY12:
    case GL_INTENSITY16:
    case GL_COMPRESSED_INTENSITY_ARB:
        tl->texFormat = &_texformat_i8;
        break;

    case GL_COLOR_INDEX:
    case GL_COLOR_INDEX1_EXT:
    case GL_COLOR_INDEX2_EXT:
    case GL_COLOR_INDEX4_EXT:
    case GL_COLOR_INDEX8_EXT:
    case GL_COLOR_INDEX12_EXT:
    case GL_COLOR_INDEX16_EXT:
        tl->texFormat = &_texformat_ci8;
        break;

    default:
        return;
    }
}

#if 0
void crStateTextureInitTexture (GLuint name) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);
    CRTextureObj *tobj;

    GET_TOBJ(tobj, name);
    if (!tobj) return;

    crStateTextureInitTextureObj(g, tobj, name, GL_NONE);
}
#endif



/**
 * Return the texture object corresponding to the given target and ID.
 */
CRTextureObj *
crStateTextureGet(GLenum target, GLuint name) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);
    CRTextureObj *tobj;

    if (name == 0)
    {
        switch (target) {
        case GL_TEXTURE_1D:
            return &t->base1D;
        case GL_TEXTURE_2D:
            return &t->base2D;
        case GL_TEXTURE_3D:
            return &t->base3D;
#ifdef CR_ARB_texture_cube_map
        case GL_TEXTURE_CUBE_MAP_ARB:
            return &t->baseCubeMap;
#endif
#ifdef CR_NV_texture_rectangle
        case GL_TEXTURE_RECTANGLE_NV:
            return &t->baseRect;
#endif
        default:
            return NULL;
        }
    }

    GET_TOBJ(tobj, g, name);

    return tobj;
}


/*
 * Allocate a new texture object with the given name.
 * Also insert into hash table.
 */
static CRTextureObj *
crStateTextureAllocate_t(CRContext *ctx, GLuint name) 
{
    CRTextureObj *tobj;

    if (!name) 
        return NULL;

    tobj = crCalloc(sizeof(CRTextureObj));
    if (!tobj)
        return NULL;

    crHashtableAdd( ctx->shared->textureTable, name, (void *) tobj );

    crStateTextureInitTextureObj(ctx, tobj, name, GL_NONE);

    return tobj;
}


/**
 * Delete all the data that hangs off a CRTextureObj, but don't
 * delete the texture object itself, since it may not have been
 * dynamically allocated.
 */
void
crStateDeleteTextureObjectData(CRTextureObj *tobj)
{
    int k;
    int face;

    CRASSERT(tobj);

    /* Free the texture images */
    for (face = 0; face < 6; face++) {
        CRTextureLevel *levels = NULL;
        levels = tobj->level[face];
        if (levels) {
            /* free all mipmap levels for this face */
            for (k = 0; k < CR_MAX_MIPMAP_LEVELS; k++) {
                CRTextureLevel *tl = levels + k;
                if (tl->img) {
                    crFree(tl->img);
                     tl->img = NULL;
                     tl->bytes = 0;
                }
            }
            crFree(levels);
        }
        tobj->level[face] = NULL;
    }
}


void
crStateDeleteTextureObject(CRTextureObj *tobj)
{
    crStateDeleteTextureObjectData(tobj);
    crFree(tobj);
}

void STATE_APIENTRY crStateGenTextures(GLsizei n, GLuint *textures) 
{
    CRContext *g = GetCurrentContext();
    GLint start;

    FLUSH();

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                                 "glGenTextures called in Begin/End");
        return;
    }

    if (n < 0)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
                                 "Negative n passed to glGenTextures: %d", n);
        return;
    }

    start = crHashtableAllocKeys(g->shared->textureTable, n);
    if (start)
    {
        GLint i;
        for (i = 0; i < n; i++)
            textures[i] = (GLuint) (start + i);
    }
    else
    {
        crStateError(__LINE__, __FILE__, GL_OUT_OF_MEMORY, "glGenTextures");
    }
}

static void crStateTextureCheckFBOAPs(GLenum target, GLuint texture)
{
    GLuint u;
    CRFBOAttachmentPoint *ap;
    CRContext *g = GetCurrentContext();
    CRFramebufferObjectState *fbo = &g->framebufferobject;
    CRFramebufferObject *pFBO;

    pFBO = GL_READ_FRAMEBUFFER==target ? fbo->readFB : fbo->drawFB;
    if (!pFBO) return;

    for (u=0; u<CR_MAX_COLOR_ATTACHMENTS; ++u)
    {
        ap = &pFBO->color[u];
        if (ap->type==GL_TEXTURE && ap->name==texture)
        {
            crStateFramebufferTexture1DEXT(target, u+GL_COLOR_ATTACHMENT0_EXT, 0, 0, 0);
        }
    }

    ap = &pFBO->depth;
    if (ap->type==GL_TEXTURE && ap->name==texture)
    {
        crStateFramebufferTexture1DEXT(target, GL_DEPTH_ATTACHMENT_EXT, 0, 0, 0);
    }

    ap = &pFBO->stencil;
    if (ap->type==GL_TEXTURE && ap->name==texture)
    {
        crStateFramebufferTexture1DEXT(target, GL_STENCIL_ATTACHMENT_EXT, 0, 0, 0);
    }
}

void STATE_APIENTRY crStateDeleteTextures(GLsizei n, const GLuint *textures) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);
    CRStateBits *sb = GetCurrentBits();
    CRTextureBits *tb = &(sb->texture);
    int i;

    FLUSH();

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                                 "glDeleteTextures called in Begin/End");
        return;
    }

    if (n < 0)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
                                 "Negative n passed to glDeleteTextures: %d", n);
        return;
    }

    for (i=0; i<n; i++) 
    {
        GLuint name = textures[i];
        CRTextureObj *tObj;
        GET_TOBJ(tObj, g, name);
        if (name && tObj)
        {
            GLuint u;
            /* remove from hashtable */
            crHashtableDelete(g->shared->textureTable, name, NULL);

            /* if the currentTexture is deleted, 
             ** reset back to the base texture.
             */
            for (u = 0; u < g->limits.maxTextureUnits; u++)
            {
                if (tObj == t->unit[u].currentTexture1D) 
                {
                    t->unit[u].currentTexture1D = &(t->base1D);
                }
                if (tObj == t->unit[u].currentTexture2D) 
                {
                    t->unit[u].currentTexture2D = &(t->base2D);
                }
#ifdef CR_OPENGL_VERSION_1_2
                if (tObj == t->unit[u].currentTexture3D) 
                {
                    t->unit[u].currentTexture3D = &(t->base3D);
                }
#endif
#ifdef CR_ARB_texture_cube_map
                if (tObj == t->unit[u].currentTextureCubeMap)
                {
                    t->unit[u].currentTextureCubeMap = &(t->baseCubeMap);
                }
#endif
#ifdef CR_NV_texture_rectangle
                if (tObj == t->unit[u].currentTextureRect)
                {
                    t->unit[u].currentTextureRect = &(t->baseRect);
                }
#endif
            }

#ifdef CR_EXT_framebuffer_object
            crStateTextureCheckFBOAPs(GL_DRAW_FRAMEBUFFER, name);
            crStateTextureCheckFBOAPs(GL_READ_FRAMEBUFFER, name);
#endif
            crStateDeleteTextureObject(tObj);
        }
    }

    DIRTY(tb->dirty, g->neg_bitid);
    DIRTY(tb->current[t->curTextureUnit], g->neg_bitid);
}



void STATE_APIENTRY crStateClientActiveTextureARB( GLenum texture )
{
    CRContext *g = GetCurrentContext();
    CRClientState *c = &(g->client);

    FLUSH();

    if (!g->extensions.ARB_multitexture) {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                                 "glClientActiveTextureARB not available");
        return;
    }

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                                 "glClientActiveTextureARB called in Begin/End");
        return;
    }

    if ( texture < GL_TEXTURE0_ARB ||
             texture >= GL_TEXTURE0_ARB + g->limits.maxTextureUnits)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                                 "crStateClientActiveTexture: unit = %d (max is %d)",
                                 texture, g->limits.maxTextureUnits );
        return;
    }

    c->curClientTextureUnit = texture - GL_TEXTURE0_ARB;

    DIRTY(GetCurrentBits()->client.dirty, g->neg_bitid);
}

void STATE_APIENTRY crStateActiveTextureARB( GLenum texture )
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);

    FLUSH();

    if (!g->extensions.ARB_multitexture) {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                                 "glActiveTextureARB not available");
        return;
    }

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION, "glActiveTextureARB called in Begin/End");
        return;
    }

    if ( texture < GL_TEXTURE0_ARB || texture >= GL_TEXTURE0_ARB + g->limits.maxTextureUnits)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION, "Bad texture unit passed to crStateActiveTexture: %d (max is %d)", texture, g->limits.maxTextureUnits );
        return;
    }

    t->curTextureUnit = texture - GL_TEXTURE0_ARB;

    /* update the current matrix pointer, etc. */
    if (g->transform.matrixMode == GL_TEXTURE) {
        crStateMatrixMode(GL_TEXTURE);
    }
}

void STATE_APIENTRY crStateBindTexture(GLenum target, GLuint texture) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);
    CRTextureObj *tobj;
    CRStateBits *sb = GetCurrentBits();
    CRTextureBits *tb = &(sb->texture);

    FLUSH();

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION, "glBindTexture called in Begin/End");
        return;
    }

    /* Special Case name = 0 */
    if (!texture) 
    {
        switch (target) 
        {
            case GL_TEXTURE_1D:
                t->unit[t->curTextureUnit].currentTexture1D = &(t->base1D);
                break;
            case GL_TEXTURE_2D:
                t->unit[t->curTextureUnit].currentTexture2D = &(t->base2D);
                break;
#ifdef CR_OPENGL_VERSION_1_2
            case GL_TEXTURE_3D:
                t->unit[t->curTextureUnit].currentTexture3D = &(t->base3D);
                break;
#endif
#ifdef CR_ARB_texture_cube_map
            case GL_TEXTURE_CUBE_MAP_ARB:
                if (!g->extensions.ARB_texture_cube_map) {
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "Invalid target passed to glBindTexture: %d", target);
                    return;
                }
                t->unit[t->curTextureUnit].currentTextureCubeMap = &(t->baseCubeMap);
                break;
#endif
#ifdef CR_NV_texture_rectangle
            case GL_TEXTURE_RECTANGLE_NV:
                if (!g->extensions.NV_texture_rectangle) {
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "Invalid target passed to glBindTexture: %d", target);
                    return;
                }
                t->unit[t->curTextureUnit].currentTextureRect = &(t->baseRect);
                break;
#endif
            default:
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "Invalid target passed to glBindTexture: %d", target);
                return;
        }

        DIRTY(tb->dirty, g->neg_bitid);
        DIRTY(tb->current[t->curTextureUnit], g->neg_bitid);
        return;
    }

    /* texture != 0 */
    /* Get the texture */
    GET_TOBJ(tobj, g, texture);
    if (!tobj)
    {
        tobj = crStateTextureAllocate_t(g, texture);
    }

    /* Check the targets */
    if (tobj->target == GL_NONE)
    {
        /* Target isn't set so set it now.*/
        tobj->target = target;
    }
    else if ((tobj->target != target) 
             && !((target==GL_TEXTURE_RECTANGLE_NV && tobj->target==GL_TEXTURE_2D)
                  ||(target==GL_TEXTURE_2D && tobj->target==GL_TEXTURE_RECTANGLE_NV)))
    {
        crWarning( "You called glBindTexture with a target of 0x%x, but the texture you wanted was target 0x%x [1D: %x 2D: %x 3D: %x cube: %x]", (int) target, (int) tobj->target, GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP );
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION, "Attempt to bind a texture of different dimensions");
        return;
    }

    /* Set the current texture */
    switch (target) 
    {
        case GL_TEXTURE_1D:
            t->unit[t->curTextureUnit].currentTexture1D = tobj;
            break;
        case GL_TEXTURE_2D:
            t->unit[t->curTextureUnit].currentTexture2D = tobj;
            break;
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_3D:
            t->unit[t->curTextureUnit].currentTexture3D = tobj;
            break;
#endif
#ifdef CR_ARB_texture_cube_map
        case GL_TEXTURE_CUBE_MAP_ARB:
            t->unit[t->curTextureUnit].currentTextureCubeMap = tobj;
            break;
#endif
#ifdef CR_NV_texture_rectangle
        case GL_TEXTURE_RECTANGLE_NV:
            t->unit[t->curTextureUnit].currentTextureRect = tobj;
            break;
#endif
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                     "Invalid target passed to glBindTexture: %d", target);
            return;
    }

    DIRTY(tb->dirty, g->neg_bitid);
    DIRTY(tb->current[t->curTextureUnit], g->neg_bitid);
}


void STATE_APIENTRY
crStateTexParameterfv(GLenum target, GLenum pname, const GLfloat *param) 
{
    CRContext *g = GetCurrentContext();
    CRTextureObj *tobj = NULL;
    CRTextureLevel *tl = NULL;
    GLenum e = (GLenum) *param;
    CRStateBits *sb = GetCurrentBits();
    CRTextureBits *tb = &(sb->texture);
    unsigned int i;

    FLUSH();

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                    "TexParameterfv called in Begin/End");
        return;
    }

    crStateGetTextureObjectAndImage(g, target, 0, &tobj, &tl);
    if (!tobj) {
        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "TexParamterfv(invalid target=0x%x)", target);
        return;
    }

    switch (pname) 
    {
        case GL_TEXTURE_MIN_FILTER:
            if (e != GL_NEAREST &&
                    e != GL_LINEAR &&
                    e != GL_NEAREST_MIPMAP_NEAREST &&
                    e != GL_LINEAR_MIPMAP_NEAREST &&
                    e != GL_NEAREST_MIPMAP_LINEAR &&
                    e != GL_LINEAR_MIPMAP_LINEAR)
            {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                            "TexParamterfv: GL_TEXTURE_MIN_FILTER invalid param: %d", e);
                return;
            }
            tobj->minFilter = e;
            break;
        case GL_TEXTURE_MAG_FILTER:
            if (e != GL_NEAREST && e != GL_LINEAR)
            {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                            "TexParamterfv: GL_TEXTURE_MAG_FILTER invalid param: %d", e);
                return;
            }
            tobj->magFilter = e;
            break;
        case GL_TEXTURE_WRAP_S:
            if (e == GL_CLAMP || e == GL_REPEAT) {
                tobj->wrapS = e;
            }
#ifdef CR_OPENGL_VERSION_1_2
            else if (e == GL_CLAMP_TO_EDGE) {
                tobj->wrapS = e;
            }
#endif
#ifdef GL_CLAMP_TO_EDGE_EXT
            else if (e == GL_CLAMP_TO_EDGE_EXT && g->extensions.EXT_texture_edge_clamp) {
                tobj->wrapS = e;
            }
#endif
#ifdef CR_ARB_texture_border_clamp
            else if (e == GL_CLAMP_TO_BORDER_ARB && g->extensions.ARB_texture_border_clamp) {
                tobj->wrapS = e;
            }
#endif
#ifdef CR_ARB_texture_mirrored_repeat
            else if (e == GL_MIRRORED_REPEAT_ARB && g->extensions.ARB_texture_mirrored_repeat) {
                tobj->wrapS = e;
            }
#endif
#ifdef CR_ATI_texture_mirror_once
            else if ((e == GL_MIRROR_CLAMP_ATI || e == GL_MIRROR_CLAMP_TO_EDGE_ATI) && g->extensions.ATI_texture_mirror_once) {
                tobj->wrapS = e;
            }
#endif
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "TexParameterfv: GL_TEXTURE_WRAP_S invalid param: 0x%x", e);
                return;
            }
            break;
        case GL_TEXTURE_WRAP_T:
            if (e == GL_CLAMP || e == GL_REPEAT) {
                tobj->wrapT = e;
            }
#ifdef CR_OPENGL_VERSION_1_2
            else if (e == GL_CLAMP_TO_EDGE) {
                tobj->wrapT = e;
            }
#endif
#ifdef GL_CLAMP_TO_EDGE_EXT
            else if (e == GL_CLAMP_TO_EDGE_EXT && g->extensions.EXT_texture_edge_clamp) {
                tobj->wrapT = e;
            }
#endif
#ifdef CR_ARB_texture_border_clamp
            else if (e == GL_CLAMP_TO_BORDER_ARB && g->extensions.ARB_texture_border_clamp) {
                tobj->wrapT = e;
            }
#endif
#ifdef CR_ARB_texture_mirrored_repeat
            else if (e == GL_MIRRORED_REPEAT_ARB && g->extensions.ARB_texture_mirrored_repeat) {
                tobj->wrapT = e;
            }
#endif
#ifdef CR_ATI_texture_mirror_once
            else if ((e == GL_MIRROR_CLAMP_ATI || e == GL_MIRROR_CLAMP_TO_EDGE_ATI) && g->extensions.ATI_texture_mirror_once) {
                tobj->wrapT = e;
            }
#endif
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "TexParameterfv: GL_TEXTURE_WRAP_T invalid param: 0x%x", e);
                return;
            }
            break;
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_WRAP_R:
            if (e == GL_CLAMP || e == GL_REPEAT) {
                tobj->wrapR = e;
            }
            else if (e == GL_CLAMP_TO_EDGE) {
                tobj->wrapR = e;
            }
#ifdef GL_CLAMP_TO_EDGE_EXT
            else if (e == GL_CLAMP_TO_EDGE_EXT && g->extensions.EXT_texture_edge_clamp) {
                tobj->wrapR = e;
            }
#endif
#ifdef CR_ARB_texture_border_clamp
            else if (e == GL_CLAMP_TO_BORDER_ARB && g->extensions.ARB_texture_border_clamp) {
                tobj->wrapR = e;
            }
#endif
#ifdef CR_ARB_texture_mirrored_repeat
            else if (e == GL_MIRRORED_REPEAT_ARB && g->extensions.ARB_texture_mirrored_repeat) {
                tobj->wrapR = e;
            }
#endif
#ifdef CR_ATI_texture_mirror_once
            else if ((e == GL_MIRROR_CLAMP_ATI || e == GL_MIRROR_CLAMP_TO_EDGE_ATI) && g->extensions.ATI_texture_mirror_once) {
                tobj->wrapR = e;
            }
#endif
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "TexParameterfv: GL_TEXTURE_WRAP_R invalid param: 0x%x", e);
                return;
            }
            break;
        case GL_TEXTURE_PRIORITY:
            tobj->priority = param[0];
            break;
        case GL_TEXTURE_MIN_LOD:
            tobj->minLod = param[0];
            break;
        case GL_TEXTURE_MAX_LOD:
            tobj->maxLod = param[0];
            break;
        case GL_TEXTURE_BASE_LEVEL:
            if (e < 0.0f) 
            {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "TexParameterfv: GL_TEXTURE_BASE_LEVEL invalid param: 0x%x", e);
                return;
            }
            tobj->baseLevel = e;
            break;
        case GL_TEXTURE_MAX_LEVEL:
            if (e < 0.0f) 
            {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "TexParameterfv: GL_TEXTURE_MAX_LEVEL invalid param: 0x%x", e);
                return;
            }
            tobj->maxLevel = e;
            break;
#endif
        case GL_TEXTURE_BORDER_COLOR:
            tobj->borderColor.r = param[0];
            tobj->borderColor.g = param[1];
            tobj->borderColor.b = param[2];
            tobj->borderColor.a = param[3];
            break;
#ifdef CR_EXT_texture_filter_anisotropic
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            if (g->extensions.EXT_texture_filter_anisotropic) {
                if (param[0] < 1.0f)
                {
                    crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
                        "TexParameterfv: GL_TEXTURE_MAX_ANISOTROPY_EXT called with parameter less than 1: %f", param[0]);
                    return;
                }
                tobj->maxAnisotropy = param[0];
                if (tobj->maxAnisotropy > g->limits.maxTextureAnisotropy)
                {
                    tobj->maxAnisotropy = g->limits.maxTextureAnisotropy;
                }
            }
            break;
#endif
#ifdef CR_ARB_depth_texture
        case GL_DEPTH_TEXTURE_MODE_ARB:
            if (g->extensions.ARB_depth_texture) {
                if (param[0] == GL_LUMINANCE || 
                    param[0] == GL_INTENSITY ||
                    param[0] == GL_ALPHA) {
                    tobj->depthMode = (GLenum) param[0];
                }
                else
                {
                    crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
                        "TexParameterfv: GL_DEPTH_TEXTURE_MODE_ARB called with invalid parameter: 0x%x", param[0]);
                    return;
                }
            }
            break;
#endif
#ifdef CR_ARB_shadow
        case GL_TEXTURE_COMPARE_MODE_ARB:
            if (g->extensions.ARB_shadow) {
                if (param[0] == GL_NONE ||
                    param[0] == GL_COMPARE_R_TO_TEXTURE_ARB) {
                    tobj->compareMode = (GLenum) param[0];
                }
                else
                {
                    crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
                        "TexParameterfv: GL_TEXTURE_COMPARE_MODE_ARB called with invalid parameter: 0x%x", param[0]);
                    return;
                }
            }
            break;
        case GL_TEXTURE_COMPARE_FUNC_ARB:
            if (g->extensions.ARB_shadow) {
                if (param[0] == GL_LEQUAL ||
                    param[0] == GL_GEQUAL) {
                    tobj->compareFunc = (GLenum) param[0];
                } 
            }
#ifdef CR_EXT_shadow_funcs
            else if (g->extensions.EXT_shadow_funcs) {
                if (param[0] == GL_LEQUAL ||
                    param[0] == GL_GEQUAL ||
                    param[0] == GL_LESS ||
                    param[0] == GL_GREATER ||
                    param[0] == GL_ALWAYS ||
                    param[0] == GL_NEVER ) {
                    tobj->compareFunc = (GLenum) param[0];
                } 
            }
#endif
            else {
                    crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
                        "TexParameterfv: GL_TEXTURE_COMPARE_FUNC_ARB called with invalid parameter: 0x%x", param[0]);
                    return;
            }
            break;
#endif
#ifdef CR_ARB_shadow_ambient
        case GL_TEXTURE_COMPARE_FAIL_VALUE_ARB:
            if (g->extensions.ARB_shadow_ambient) {
                tobj->compareFailValue = param[0];
            }
            break;
#endif
#ifdef CR_SGIS_generate_mipmap
        case GL_GENERATE_MIPMAP_SGIS:
            if (g->extensions.SGIS_generate_mipmap) {
                tobj->generateMipmap = param[0] ? GL_TRUE : GL_FALSE;
            }
            break;
#endif
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                "TexParamterfv: Invalid pname: %d", pname);
            return;
    }

    DIRTY(tobj->dirty, g->neg_bitid);
    for (i = 0; i < g->limits.maxTextureUnits; i++)
    {
        DIRTY(tobj->paramsBit[i], g->neg_bitid);
    }
    DIRTY(tb->dirty, g->neg_bitid);
}


void STATE_APIENTRY
crStateTexParameteriv(GLenum target, GLenum pname, const GLint *param) 
{
    GLfloat f_param;
    GLcolor f_color;
    switch (pname) 
    {
        case GL_TEXTURE_MIN_FILTER:
        case GL_TEXTURE_MAG_FILTER:
        case GL_TEXTURE_WRAP_S:
        case GL_TEXTURE_WRAP_T:
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_WRAP_R:
        case GL_TEXTURE_PRIORITY:
        case GL_TEXTURE_MIN_LOD:
        case GL_TEXTURE_MAX_LOD:
        case GL_TEXTURE_BASE_LEVEL:
        case GL_TEXTURE_MAX_LEVEL:
#endif
#ifdef CR_EXT_texture_filter_anisotropic
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
#endif
#ifdef CR_ARB_depth_texture
        case GL_DEPTH_TEXTURE_MODE_ARB:
#endif
#ifdef CR_ARB_shadow
        case GL_TEXTURE_COMPARE_MODE_ARB:
        case GL_TEXTURE_COMPARE_FUNC_ARB:
#endif
#ifdef CR_ARB_shadow_ambinet
        case GL_TEXTURE_COMPARE_FAIL_VALUE_ARB:
#endif
#ifdef CR_SGIS_generate_mipmap
        case GL_GENERATE_MIPMAP_SGIS:
#endif
            f_param = (GLfloat) (*param);
            crStateTexParameterfv( target, pname, &(f_param) );
            break;
        case GL_TEXTURE_BORDER_COLOR:
            f_color.r = ((GLfloat) param[0])/CR_MAXINT;
            f_color.g = ((GLfloat) param[1])/CR_MAXINT;
            f_color.b = ((GLfloat) param[2])/CR_MAXINT;
            f_color.a = ((GLfloat) param[3])/CR_MAXINT;
            crStateTexParameterfv( target, pname, (const GLfloat *) &(f_color) );
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                "TexParamteriv: Invalid pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateTexParameterf(GLenum target, GLenum pname, GLfloat param) 
{
    crStateTexParameterfv( target, pname, &param );
}


void STATE_APIENTRY
crStateTexParameteri(GLenum target, GLenum pname, GLint param) {
    GLfloat f_param = (GLfloat) param;
    crStateTexParameterfv( target, pname, &f_param );
}


void STATE_APIENTRY
crStateTexEnvfv(GLenum target, GLenum pname, const GLfloat *param) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);
    CRStateBits *sb = GetCurrentBits();
    CRTextureBits *tb = &(sb->texture);
    GLenum e;
    GLcolorf c;
    GLuint stage = 0;

    (void) stage;

    FLUSH();

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                    "glTexEnvfv called in begin/end");
        return;
    }

#if CR_EXT_texture_lod_bias
    if (target == GL_TEXTURE_FILTER_CONTROL_EXT) {
        if (!g->extensions.EXT_texture_lod_bias || pname != GL_TEXTURE_LOD_BIAS_EXT) {
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glTexEnv");
        }
        else {
            t->unit[t->curTextureUnit].lodBias = *param;
            DIRTY(tb->envBit[t->curTextureUnit], g->neg_bitid);
            DIRTY(tb->dirty, g->neg_bitid);
        }
        return;
    }
    else
#endif
#if CR_ARB_point_sprite
    if (target == GL_POINT_SPRITE_ARB) {
        if (!g->extensions.ARB_point_sprite || pname != GL_COORD_REPLACE_ARB) {
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glTexEnv");
        }
        else {
            CRPointBits *pb = &(sb->point);
            g->point.coordReplacement[t->curTextureUnit] = *param ? GL_TRUE : GL_FALSE;
            DIRTY(pb->coordReplacement[t->curTextureUnit], g->neg_bitid);
            DIRTY(pb->dirty, g->neg_bitid);
        }
        return;
    }
    else
#endif
    if (target != GL_TEXTURE_ENV)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glTexEnvfv: target != GL_TEXTURE_ENV: %d", target);
        return;
    }

    switch (pname) 
    {
        case GL_TEXTURE_ENV_MODE:
            e = (GLenum) *param;
            if (e != GL_MODULATE &&
                    e != GL_DECAL &&
                    e != GL_BLEND &&
                    e != GL_ADD &&
                    e != GL_REPLACE &&
                    e != GL_COMBINE_ARB)
            {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                            "glTexEnvfv: invalid param: %f", *param);
                return;
            }
            t->unit[t->curTextureUnit].envMode = e;
            break;
        case GL_TEXTURE_ENV_COLOR:
            c.r = param[0];
            c.g = param[1];
            c.b = param[2];
            c.a = param[3];
            if (c.r > 1.0f) c.r = 1.0f;
            if (c.g > 1.0f) c.g = 1.0f;
            if (c.b > 1.0f) c.b = 1.0f;
            if (c.a > 1.0f) c.a = 1.0f;
            if (c.r < 0.0f) c.r = 0.0f;
            if (c.g < 0.0f) c.g = 0.0f;
            if (c.b < 0.0f) c.b = 0.0f;
            if (c.a < 0.0f) c.a = 0.0f;
            t->unit[t->curTextureUnit].envColor = c;
            break;

#ifdef CR_ARB_texture_env_combine
        case GL_COMBINE_RGB_ARB:
            e = (GLenum) (GLint) *param;
            if (g->extensions.ARB_texture_env_combine &&
                    (e == GL_REPLACE ||
                     e == GL_MODULATE ||
                     e == GL_ADD ||
                     e == GL_ADD_SIGNED_ARB ||
                     e == GL_INTERPOLATE_ARB ||
                     e == GL_SUBTRACT_ARB)) {
                 t->unit[t->curTextureUnit].combineModeRGB = e;
            }
#ifdef CR_ARB_texture_env_dot3
            else if (g->extensions.ARB_texture_env_dot3 &&
                             (e == GL_DOT3_RGB_ARB ||
                                e == GL_DOT3_RGBA_ARB ||
                                e == GL_DOT3_RGB_EXT ||
                                e == GL_DOT3_RGBA_EXT)) {
                 t->unit[t->curTextureUnit].combineModeRGB = e;
            }
#endif
            else {
                 crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glTexEnvfv(param=0x%x", e);
                 return;
            }
            break;
        case GL_COMBINE_ALPHA_EXT:
            e = (GLenum) *param;
            if (g->extensions.ARB_texture_env_combine &&
                    (e == GL_REPLACE ||
                     e == GL_MODULATE ||
                     e == GL_ADD ||
                     e == GL_ADD_SIGNED_ARB ||
                     e == GL_INTERPOLATE_ARB ||
                     e == GL_SUBTRACT_ARB)) {
                 t->unit[t->curTextureUnit].combineModeA = e;
            }
            else {
                 crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glTexEnvfv");
                 return;
            }
            break;
        case GL_SOURCE0_RGB_ARB:
        case GL_SOURCE1_RGB_ARB:
        case GL_SOURCE2_RGB_ARB:
            e = (GLenum) *param;
            stage = pname - GL_SOURCE0_RGB_ARB;
            if (g->extensions.ARB_texture_env_combine &&
                    (e == GL_TEXTURE ||
                     e == GL_CONSTANT_ARB ||
                     e == GL_PRIMARY_COLOR_ARB ||
                     e == GL_PREVIOUS_ARB)) {
                t->unit[t->curTextureUnit].combineSourceRGB[stage] = e;
            }
            else if (g->extensions.ARB_texture_env_crossbar &&
                             e >= GL_TEXTURE0_ARB &&
                             e < GL_TEXTURE0_ARB + g->limits.maxTextureUnits) {
                t->unit[t->curTextureUnit].combineSourceRGB[stage] = e;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glTexEnvfv");
                return;
            }
            break;
        case GL_SOURCE0_ALPHA_ARB:
        case GL_SOURCE1_ALPHA_ARB:
        case GL_SOURCE2_ALPHA_ARB:
            e = (GLenum) *param;
            stage = pname - GL_SOURCE0_ALPHA_ARB;
            if (g->extensions.ARB_texture_env_combine &&
                    (e == GL_TEXTURE ||
                     e == GL_CONSTANT_ARB ||
                     e == GL_PRIMARY_COLOR_ARB ||
                     e == GL_PREVIOUS_ARB)) {
                t->unit[t->curTextureUnit].combineSourceA[stage] = e;
            }
            else if (g->extensions.ARB_texture_env_crossbar &&
                             e >= GL_TEXTURE0_ARB &&
                             e < GL_TEXTURE0_ARB + g->limits.maxTextureUnits) {
                t->unit[t->curTextureUnit].combineSourceA[stage] = e;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glTexEnvfv");
                return;
            }
            break;
        case GL_OPERAND0_RGB_ARB:
        case GL_OPERAND1_RGB_ARB:
        case GL_OPERAND2_RGB_ARB:
            e = (GLenum) *param;
            stage = pname - GL_OPERAND0_RGB_ARB;
            if (g->extensions.ARB_texture_env_combine &&
                    (e == GL_SRC_COLOR ||
                     e == GL_ONE_MINUS_SRC_COLOR ||
                     e == GL_SRC_ALPHA ||
                     e == GL_ONE_MINUS_SRC_ALPHA)) {
                t->unit[t->curTextureUnit].combineOperandRGB[stage] = e;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glTexEnvfv");
                return;
            }
            break;
        case GL_OPERAND0_ALPHA_ARB:
        case GL_OPERAND1_ALPHA_ARB:
        case GL_OPERAND2_ALPHA_ARB:
            e = (GLenum) *param;
            stage = pname - GL_OPERAND0_ALPHA_ARB;
            if (g->extensions.ARB_texture_env_combine &&
                    (e == GL_SRC_ALPHA ||
                     e == GL_ONE_MINUS_SRC_ALPHA)) {
                t->unit[t->curTextureUnit].combineOperandA[stage] = e;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glTexEnvfv(param=0x%x)", e);
                return;
            }
            break;
        case GL_RGB_SCALE_ARB:
            if (g->extensions.ARB_texture_env_combine &&
                    (*param == 1.0 ||
                     *param == 2.0 ||
                     *param == 4.0)) {
                t->unit[t->curTextureUnit].combineScaleRGB = *param;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_VALUE, "glTexEnvfv");
                return;
            }
            break;
        case GL_ALPHA_SCALE:
            if (g->extensions.ARB_texture_env_combine &&
                    (*param == 1.0 ||
                     *param == 2.0 ||
                     *param == 4.0)) {
                t->unit[t->curTextureUnit].combineScaleA = *param;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_VALUE, "glTexEnvfv");
                return;
            }
        break;
#endif /* CR_ARB_texture_env_combine */

        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glTexEnvfv: invalid pname: %d", pname);
            return;
    }

    DIRTY(tb->envBit[t->curTextureUnit], g->neg_bitid);
    DIRTY(tb->dirty, g->neg_bitid);
}


void STATE_APIENTRY
crStateTexEnviv(GLenum target, GLenum pname, const GLint *param) 
{
    GLfloat f_param;
    GLcolor f_color;

    switch (pname) {
        case GL_TEXTURE_ENV_MODE:
            f_param = (GLfloat) (*param);
            crStateTexEnvfv( target, pname, &f_param );
            break;
        case GL_TEXTURE_ENV_COLOR:
            f_color.r = ((GLfloat) param[0]) / CR_MAXINT;
            f_color.g = ((GLfloat) param[1]) / CR_MAXINT;
            f_color.b = ((GLfloat) param[2]) / CR_MAXINT;
            f_color.a = ((GLfloat) param[3]) / CR_MAXINT;
            crStateTexEnvfv( target, pname, (const GLfloat *) &f_color );
            break;
#ifdef CR_ARB_texture_env_combine
        case GL_COMBINE_RGB_ARB:
        case GL_COMBINE_ALPHA_EXT:
        case GL_SOURCE0_RGB_ARB:
        case GL_SOURCE1_RGB_ARB:
        case GL_SOURCE2_RGB_ARB:
        case GL_SOURCE0_ALPHA_ARB:
        case GL_SOURCE1_ALPHA_ARB:
        case GL_SOURCE2_ALPHA_ARB:
        case GL_OPERAND0_RGB_ARB:
        case GL_OPERAND1_RGB_ARB:
        case GL_OPERAND2_RGB_ARB:
        case GL_OPERAND0_ALPHA_ARB:
        case GL_OPERAND1_ALPHA_ARB:
        case GL_OPERAND2_ALPHA_ARB:
        case GL_RGB_SCALE_ARB:
        case GL_ALPHA_SCALE:
            f_param = (GLfloat) (*param);
            crStateTexEnvfv( target, pname, &f_param );
            break;
#endif
#ifdef CR_EXT_texture_lod_bias
        case GL_TEXTURE_LOD_BIAS_EXT:
            f_param = (GLfloat) (*param);
            crStateTexEnvfv( target, pname, &f_param);
            break;
#endif
#ifdef CR_ARB_point_sprite
        case GL_COORD_REPLACE_ARB:
            f_param = (GLfloat) *param;
            crStateTexEnvfv( target, pname, &f_param);
            break;
#endif

        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glTexEnvfv: invalid pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateTexEnvf(GLenum target, GLenum pname, GLfloat param) 
{
    crStateTexEnvfv( target, pname, &param );
}


void STATE_APIENTRY
crStateTexEnvi(GLenum target, GLenum pname, GLint param) 
{
    GLfloat f_param = (GLfloat) param;
    crStateTexEnvfv( target, pname, &f_param );
}


void STATE_APIENTRY
crStateGetTexEnvfv(GLenum target, GLenum pname, GLfloat *param) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__,GL_INVALID_OPERATION,
                    "glGetTexEnvfv called in begin/end");
        return;
    }

#if CR_EXT_texture_lod_bias
    if (target == GL_TEXTURE_FILTER_CONTROL_EXT) {
        if (!g->extensions.EXT_texture_lod_bias || pname != GL_TEXTURE_LOD_BIAS_EXT) {
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnv");
        }
        else {
            *param = t->unit[t->curTextureUnit].lodBias;
        }
        return;
    }
    else
#endif
#if CR_ARB_point_sprite
    if (target == GL_POINT_SPRITE_ARB) {
        if (!g->extensions.ARB_point_sprite || pname != GL_COORD_REPLACE_ARB) {
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnv");
        }
        else {
            *param = (GLfloat) g->point.coordReplacement[t->curTextureUnit];
        }
        return;
    }
    else
#endif
    if (target != GL_TEXTURE_ENV)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexEnvfv: target != GL_TEXTURE_ENV: %d", target);
        return;
    }

    switch (pname) {
        case GL_TEXTURE_ENV_MODE:
            *param = (GLfloat) t->unit[t->curTextureUnit].envMode;
            break;
        case GL_TEXTURE_ENV_COLOR:
            param[0] = t->unit[t->curTextureUnit].envColor.r;
            param[1] = t->unit[t->curTextureUnit].envColor.g;
            param[2] = t->unit[t->curTextureUnit].envColor.b;
            param[3] = t->unit[t->curTextureUnit].envColor.a;
            break;
        case GL_COMBINE_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineModeRGB;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_COMBINE_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineModeA;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_SOURCE0_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineSourceRGB[0];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_SOURCE1_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineSourceRGB[1];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_SOURCE2_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineSourceRGB[2];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_SOURCE0_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineSourceA[0];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_SOURCE1_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineSourceA[1];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_SOURCE2_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineSourceA[2];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_OPERAND0_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineOperandRGB[0];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_OPERAND1_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineOperandRGB[1];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_OPERAND2_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineOperandRGB[2];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_OPERAND0_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineOperandA[0];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_OPERAND1_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineOperandA[1];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_OPERAND2_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLfloat) t->unit[t->curTextureUnit].combineOperandA[2];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_RGB_SCALE_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = t->unit[t->curTextureUnit].combineScaleRGB;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        case GL_ALPHA_SCALE:
            if (g->extensions.ARB_texture_env_combine) {
                *param = t->unit[t->curTextureUnit].combineScaleA;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnvfv(pname)");
                return;
            }
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glGetTexEnvfv: invalid pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateGetTexEnviv(GLenum target, GLenum pname, GLint *param) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__,GL_INVALID_OPERATION,
                    "glGetTexEnviv called in begin/end");
        return;
    }

#if CR_EXT_texture_lod_bias
    if (target == GL_TEXTURE_FILTER_CONTROL_EXT) {
        if (!g->extensions.EXT_texture_lod_bias || pname != GL_TEXTURE_LOD_BIAS_EXT) {
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnv");
        }
        else {
            *param = (GLint) t->unit[t->curTextureUnit].lodBias;
        }
        return;
    }
    else
#endif
#if CR_ARB_point_sprite
    if (target == GL_POINT_SPRITE_ARB) {
        if (!g->extensions.ARB_point_sprite || pname != GL_COORD_REPLACE_ARB) {
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnv");
        }
        else {
            *param = (GLint) g->point.coordReplacement[t->curTextureUnit];
        }
        return;
    }
    else
#endif
    if (target != GL_TEXTURE_ENV)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexEnviv: target != GL_TEXTURE_ENV: %d", target);
        return;
    }

    switch (pname) {
        case GL_TEXTURE_ENV_MODE:
            *param = (GLint) t->unit[t->curTextureUnit].envMode;
            break;
        case GL_TEXTURE_ENV_COLOR:
            param[0] = (GLint) (t->unit[t->curTextureUnit].envColor.r * CR_MAXINT);
            param[1] = (GLint) (t->unit[t->curTextureUnit].envColor.g * CR_MAXINT);
            param[2] = (GLint) (t->unit[t->curTextureUnit].envColor.b * CR_MAXINT);
            param[3] = (GLint) (t->unit[t->curTextureUnit].envColor.a * CR_MAXINT);
            break;
        case GL_COMBINE_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineModeRGB;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_COMBINE_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineModeA;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_SOURCE0_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineSourceRGB[0];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_SOURCE1_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineSourceRGB[1];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_SOURCE2_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineSourceRGB[2];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_SOURCE0_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineSourceA[0];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_SOURCE1_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineSourceA[1];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_SOURCE2_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineSourceA[2];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_OPERAND0_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineOperandRGB[0];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_OPERAND1_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineOperandRGB[1];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_OPERAND2_RGB_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineOperandRGB[2];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_OPERAND0_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineOperandA[0];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_OPERAND1_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineOperandA[1];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_OPERAND2_ALPHA_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineOperandA[2];
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_RGB_SCALE_ARB:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineScaleRGB;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        case GL_ALPHA_SCALE:
            if (g->extensions.ARB_texture_env_combine) {
                *param = (GLint) t->unit[t->curTextureUnit].combineScaleA;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, "glGetTexEnviv(pname)");
                return;
            }
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glGetTexEnviv: invalid pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateTexGendv(GLenum coord, GLenum pname, const GLdouble *param) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);
    CRTransformState *trans = &(g->transform);
    GLvectorf v;
    GLenum e;
    CRmatrix inv;
    CRStateBits *sb = GetCurrentBits();
    CRTextureBits *tb = &(sb->texture);

    FLUSH();

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                    "glTexGen called in begin/end");
        return;
    }

    switch (coord) 
    {
        case GL_S:
            switch (pname) 
            {
                case GL_TEXTURE_GEN_MODE:
                    e = (GLenum) *param;
                    if (e == GL_OBJECT_LINEAR ||
                        e == GL_EYE_LINEAR ||
                        e == GL_SPHERE_MAP
#if defined(GL_ARB_texture_cube_map) || defined(GL_EXT_texture_cube_map) || defined(GL_NV_texgen_reflection)
                        || ((e == GL_REFLECTION_MAP_ARB || e == GL_NORMAL_MAP_ARB)
                            && g->extensions.ARB_texture_cube_map)
#endif
                    ) {
                        t->unit[t->curTextureUnit].gen.s = e;
                        DIRTY(tb->genMode[t->curTextureUnit], g->neg_bitid);
                        DIRTY(tb->dirty, g->neg_bitid);
                    }
                    else {
                        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                            "glTexGendv called with bad param: %lf", *param);
                        return;
                    }
                    break;
                case GL_OBJECT_PLANE:
                    v.x = (GLfloat) param[0];
                    v.y = (GLfloat) param[1];
                    v.z = (GLfloat) param[2];
                    v.w = (GLfloat) param[3];
                    t->unit[t->curTextureUnit].objSCoeff = v;
                    DIRTY(tb->objGen[t->curTextureUnit], g->neg_bitid);
                    DIRTY(tb->dirty, g->neg_bitid);
                    break;
                case GL_EYE_PLANE:
                    v.x = (GLfloat) param[0];
                    v.y = (GLfloat) param[1];
                    v.z = (GLfloat) param[2];
                    v.w = (GLfloat) param[3];
                    crMatrixInvertTranspose(&inv, trans->modelViewStack.top);
                    crStateTransformXformPointMatrixf(&inv, &v);
                    t->unit[t->curTextureUnit].eyeSCoeff = v;
                    DIRTY(tb->eyeGen[t->curTextureUnit], g->neg_bitid);
                    DIRTY(tb->dirty, g->neg_bitid);
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glTexGendv called with bogus pname: %d", pname);
                    return;
            }
            break;
        case GL_T:
            switch (pname) {
                case GL_TEXTURE_GEN_MODE:
                    e = (GLenum) *param;
                    if (e == GL_OBJECT_LINEAR ||
                        e == GL_EYE_LINEAR ||
                        e == GL_SPHERE_MAP
#if defined(GL_ARB_texture_cube_map) || defined(GL_EXT_texture_cube_map) || defined(GL_NV_texgen_reflection)
                        || ((e == GL_REFLECTION_MAP_ARB || e == GL_NORMAL_MAP_ARB)
                            && g->extensions.ARB_texture_cube_map)
#endif
                    ) {
                        t->unit[t->curTextureUnit].gen.t = e;
                        DIRTY(tb->genMode[t->curTextureUnit], g->neg_bitid);
                        DIRTY(tb->dirty, g->neg_bitid);
                    }
                    else {
                        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                            "glTexGendv called with bad param: %lf", *param);
                        return;
                    }
                    break;
                case GL_OBJECT_PLANE:
                    v.x = (GLfloat) param[0];
                    v.y = (GLfloat) param[1];
                    v.z = (GLfloat) param[2];
                    v.w = (GLfloat) param[3];
                    t->unit[t->curTextureUnit].objTCoeff = v;
                    DIRTY(tb->objGen[t->curTextureUnit], g->neg_bitid);
                    DIRTY(tb->dirty, g->neg_bitid);
                    break;
                case GL_EYE_PLANE:
                    v.x = (GLfloat) param[0];
                    v.y = (GLfloat) param[1];
                    v.z = (GLfloat) param[2];
                    v.w = (GLfloat) param[3];
                    crMatrixInvertTranspose(&inv, trans->modelViewStack.top);
                    crStateTransformXformPointMatrixf(&inv, &v);
                    t->unit[t->curTextureUnit].eyeTCoeff = v;
                    DIRTY(tb->eyeGen[t->curTextureUnit], g->neg_bitid);
                    DIRTY(tb->dirty, g->neg_bitid);
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glTexGen called with bogus pname: %d", pname);
                    return;
            }
            break;
        case GL_R:
            switch (pname) {
                case GL_TEXTURE_GEN_MODE:
                    e = (GLenum) *param;
                    if (e == GL_OBJECT_LINEAR ||
                        e == GL_EYE_LINEAR ||
                        e == GL_SPHERE_MAP
#if defined(GL_ARB_texture_cube_map) || defined(GL_EXT_texture_cube_map) || defined(GL_NV_texgen_reflection)
                        || ((e == GL_REFLECTION_MAP_ARB || e == GL_NORMAL_MAP_ARB)
                            && g->extensions.ARB_texture_cube_map)
#endif
                    ) {
                        t->unit[t->curTextureUnit].gen.r = e;
                        DIRTY(tb->genMode[t->curTextureUnit], g->neg_bitid);
                        DIRTY(tb->dirty, g->neg_bitid);
                    }
                    else {
                        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                            "glTexGen called with bad param: %lf", *param);
                        return;
                    }
                    break;
                case GL_OBJECT_PLANE:
                    v.x = (GLfloat) param[0];
                    v.y = (GLfloat) param[1];
                    v.z = (GLfloat) param[2];
                    v.w = (GLfloat) param[3];
                    t->unit[t->curTextureUnit].objRCoeff = v;
                    DIRTY(tb->objGen[t->curTextureUnit], g->neg_bitid);
                    DIRTY(tb->dirty, g->neg_bitid);
                    break;
                case GL_EYE_PLANE:
                    v.x = (GLfloat) param[0];
                    v.y = (GLfloat) param[1];
                    v.z = (GLfloat) param[2];
                    v.w = (GLfloat) param[3];
                    crMatrixInvertTranspose(&inv, trans->modelViewStack.top);
                    crStateTransformXformPointMatrixf(&inv, &v);
                    t->unit[t->curTextureUnit].eyeRCoeff = v;
                    DIRTY(tb->eyeGen[t->curTextureUnit], g->neg_bitid);
                    DIRTY(tb->dirty, g->neg_bitid);
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glTexGen called with bogus pname: %d", pname);
                    return;
            }
            break;
        case GL_Q:
            switch (pname) {
                case GL_TEXTURE_GEN_MODE:
                    e = (GLenum) *param;
                    if (e == GL_OBJECT_LINEAR ||
                        e == GL_EYE_LINEAR ||
                        e == GL_SPHERE_MAP
#if defined(GL_ARB_texture_cube_map) || defined(GL_EXT_texture_cube_map) || defined(GL_NV_texgen_reflection)
                        || ((e == GL_REFLECTION_MAP_ARB || e == GL_NORMAL_MAP_ARB)
                            && g->extensions.ARB_texture_cube_map)
#endif
                    ) {
                        t->unit[t->curTextureUnit].gen.q = e;
                        DIRTY(tb->genMode[t->curTextureUnit], g->neg_bitid);
                        DIRTY(tb->dirty, g->neg_bitid);
                    }
                    else {
                        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                            "glTexGen called with bad param: %lf", *param);
                        return;
                    }
                    break;
                case GL_OBJECT_PLANE:
                    v.x = (GLfloat) param[0];
                    v.y = (GLfloat) param[1];
                    v.z = (GLfloat) param[2];
                    v.w = (GLfloat) param[3];
                    t->unit[t->curTextureUnit].objQCoeff = v;
                    DIRTY(tb->objGen[t->curTextureUnit], g->neg_bitid);
                    DIRTY(tb->dirty, g->neg_bitid);
                    break;
                case GL_EYE_PLANE:
                    v.x = (GLfloat) param[0];
                    v.y = (GLfloat) param[1];
                    v.z = (GLfloat) param[2];
                    v.w = (GLfloat) param[3];
                    crMatrixInvertTranspose(&inv, trans->modelViewStack.top);
                    crStateTransformXformPointMatrixf(&inv, &v);
                    t->unit[t->curTextureUnit].eyeQCoeff = v;
                    DIRTY(tb->eyeGen[t->curTextureUnit], g->neg_bitid);
                    DIRTY(tb->dirty, g->neg_bitid);
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glTexGen called with bogus pname: %d", pname);
                    return;
            }
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glTexGen called with bogus coord: %d", coord);
            return;
    }
}


void STATE_APIENTRY
crStateTexGenfv(GLenum coord, GLenum pname, const GLfloat *param) 
{
    GLdouble d_param;
    GLvectord d_vector;
    switch (pname) 
    {
        case GL_TEXTURE_GEN_MODE:
            d_param = (GLdouble) *param;
            crStateTexGendv( coord, pname, &d_param );
            break;
        case GL_OBJECT_PLANE:
        case GL_EYE_PLANE:
            d_vector.x = (GLdouble) param[0];
            d_vector.y = (GLdouble) param[1];
            d_vector.z = (GLdouble) param[2];
            d_vector.w = (GLdouble) param[3];
            crStateTexGendv( coord, pname, (const double *) &d_vector );
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glTexGen called with bogus pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateTexGeniv(GLenum coord, GLenum pname, const GLint *param) 
{
    GLdouble d_param;
    GLvectord d_vector;
    switch (pname) 
    {
        case GL_TEXTURE_GEN_MODE:
            d_param = (GLdouble) *param;
            crStateTexGendv( coord, pname, &d_param );
            break;
        case GL_OBJECT_PLANE:
        case GL_EYE_PLANE:
            d_vector.x = (GLdouble) param[0];
            d_vector.y = (GLdouble) param[1];
            d_vector.z = (GLdouble) param[2];
            d_vector.w = (GLdouble) param[3];
            crStateTexGendv( coord, pname, (const double *) &d_vector );
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glTexGen called with bogus pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateTexGend (GLenum coord, GLenum pname, GLdouble param) 
{
    crStateTexGendv( coord, pname, &param );
}


void STATE_APIENTRY
crStateTexGenf(GLenum coord, GLenum pname, GLfloat param) 
{
    GLdouble d_param = (GLdouble) param;
    crStateTexGendv( coord, pname, &d_param );
}


void STATE_APIENTRY
crStateTexGeni(GLenum coord, GLenum pname, GLint param) 
{
    GLdouble d_param = (GLdouble) param;
    crStateTexGendv( coord, pname, &d_param );
}


void STATE_APIENTRY
crStateGetTexGendv(GLenum coord, GLenum pname, GLdouble *param) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                    "glGetTexGen called in begin/end");
        return;
    }

    switch (pname) {
        case GL_TEXTURE_GEN_MODE:
            switch (coord) {
                case GL_S:
                    *param = (GLdouble) t->unit[t->curTextureUnit].gen.s;
                    break;
                case GL_T:
                    *param = (GLdouble) t->unit[t->curTextureUnit].gen.t;
                    break;
                case GL_R:
                    *param = (GLdouble) t->unit[t->curTextureUnit].gen.r;
                    break;
                case GL_Q:
                    *param = (GLdouble) t->unit[t->curTextureUnit].gen.q;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGen called with bogus coord: %d", coord);
                    return;
            }
            break;
        case GL_OBJECT_PLANE:
            switch (coord) {
                case GL_S:
                    param[0] = (GLdouble) t->unit[t->curTextureUnit].objSCoeff.x;
                    param[1] = (GLdouble) t->unit[t->curTextureUnit].objSCoeff.y;
                    param[2] = (GLdouble) t->unit[t->curTextureUnit].objSCoeff.z;
                    param[3] = (GLdouble) t->unit[t->curTextureUnit].objSCoeff.w;
                    break;
                case GL_T:
                    param[0] = (GLdouble) t->unit[t->curTextureUnit].objTCoeff.x;
                    param[1] = (GLdouble) t->unit[t->curTextureUnit].objTCoeff.y;
                    param[2] = (GLdouble) t->unit[t->curTextureUnit].objTCoeff.z;
                    param[3] = (GLdouble) t->unit[t->curTextureUnit].objTCoeff.w;
                    break;
                case GL_R:
                    param[0] = (GLdouble) t->unit[t->curTextureUnit].objRCoeff.x;
                    param[1] = (GLdouble) t->unit[t->curTextureUnit].objRCoeff.y;
                    param[2] = (GLdouble) t->unit[t->curTextureUnit].objRCoeff.z;
                    param[3] = (GLdouble) t->unit[t->curTextureUnit].objRCoeff.w;
                    break;
                case GL_Q:
                    param[0] = (GLdouble) t->unit[t->curTextureUnit].objQCoeff.x;
                    param[1] = (GLdouble) t->unit[t->curTextureUnit].objQCoeff.y;
                    param[2] = (GLdouble) t->unit[t->curTextureUnit].objQCoeff.z;
                    param[3] = (GLdouble) t->unit[t->curTextureUnit].objQCoeff.w;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGen called with bogus coord: %d", coord);
                    return;
            }
            break;
        case GL_EYE_PLANE:
            switch (coord) {
                case GL_S:
                    param[0] = (GLdouble) t->unit[t->curTextureUnit].eyeSCoeff.x;
                    param[1] = (GLdouble) t->unit[t->curTextureUnit].eyeSCoeff.y;
                    param[2] = (GLdouble) t->unit[t->curTextureUnit].eyeSCoeff.z;
                    param[3] = (GLdouble) t->unit[t->curTextureUnit].eyeSCoeff.w;
                    break;
                case GL_T:
                    param[0] = (GLdouble) t->unit[t->curTextureUnit].eyeTCoeff.x;
                    param[1] = (GLdouble) t->unit[t->curTextureUnit].eyeTCoeff.y;
                    param[2] = (GLdouble) t->unit[t->curTextureUnit].eyeTCoeff.z;
                    param[3] = (GLdouble) t->unit[t->curTextureUnit].eyeTCoeff.w;
                    break;
                case GL_R:
                    param[0] = (GLdouble) t->unit[t->curTextureUnit].eyeRCoeff.x;
                    param[1] = (GLdouble) t->unit[t->curTextureUnit].eyeRCoeff.y;
                    param[2] = (GLdouble) t->unit[t->curTextureUnit].eyeRCoeff.z;
                    param[3] = (GLdouble) t->unit[t->curTextureUnit].eyeRCoeff.w;
                    break;
                case GL_Q:
                    param[0] = (GLdouble) t->unit[t->curTextureUnit].eyeQCoeff.x;
                    param[1] = (GLdouble) t->unit[t->curTextureUnit].eyeQCoeff.y;
                    param[2] = (GLdouble) t->unit[t->curTextureUnit].eyeQCoeff.z;
                    param[3] = (GLdouble) t->unit[t->curTextureUnit].eyeQCoeff.w;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGen called with bogus coord: %d", coord);
                    return;
            }
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glGetTexGen called with bogus pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateGetTexGenfv(GLenum coord, GLenum pname, GLfloat *param)
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                    "glGetTexGenfv called in begin/end");
        return;
    }

    switch (pname) {
        case GL_TEXTURE_GEN_MODE:
            switch (coord) {
                case GL_S:
                    *param = (GLfloat) t->unit[t->curTextureUnit].gen.s;
                    break;
                case GL_T:
                    *param = (GLfloat) t->unit[t->curTextureUnit].gen.t;
                    break;
                case GL_R:
                    *param = (GLfloat) t->unit[t->curTextureUnit].gen.r;
                    break;
                case GL_Q:
                    *param = (GLfloat) t->unit[t->curTextureUnit].gen.q;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGenfv called with bogus coord: %d", coord);
                    return;
            }
            break;
        case GL_OBJECT_PLANE:
            switch (coord) {
                case GL_S:
                    param[0] = t->unit[t->curTextureUnit].objSCoeff.x;
                    param[1] = t->unit[t->curTextureUnit].objSCoeff.y;
                    param[2] = t->unit[t->curTextureUnit].objSCoeff.z;
                    param[3] = t->unit[t->curTextureUnit].objSCoeff.w;
                    break;
                case GL_T:
                    param[0] =  t->unit[t->curTextureUnit].objTCoeff.x;
                    param[1] =  t->unit[t->curTextureUnit].objTCoeff.y;
                    param[2] =  t->unit[t->curTextureUnit].objTCoeff.z;
                    param[3] =  t->unit[t->curTextureUnit].objTCoeff.w;
                    break;
                case GL_R:
                    param[0] =  t->unit[t->curTextureUnit].objRCoeff.x;
                    param[1] =  t->unit[t->curTextureUnit].objRCoeff.y;
                    param[2] =  t->unit[t->curTextureUnit].objRCoeff.z;
                    param[3] =  t->unit[t->curTextureUnit].objRCoeff.w;
                    break;
                case GL_Q:
                    param[0] =  t->unit[t->curTextureUnit].objQCoeff.x;
                    param[1] =  t->unit[t->curTextureUnit].objQCoeff.y;
                    param[2] =  t->unit[t->curTextureUnit].objQCoeff.z;
                    param[3] =  t->unit[t->curTextureUnit].objQCoeff.w;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGenfv called with bogus coord: %d", coord);
                    return;
            }
            break;
        case GL_EYE_PLANE:
            switch (coord) {
                case GL_S:
                    param[0] =  t->unit[t->curTextureUnit].eyeSCoeff.x;
                    param[1] =  t->unit[t->curTextureUnit].eyeSCoeff.y;
                    param[2] =  t->unit[t->curTextureUnit].eyeSCoeff.z;
                    param[3] =  t->unit[t->curTextureUnit].eyeSCoeff.w;
                    break;
                case GL_T:
                    param[0] =  t->unit[t->curTextureUnit].eyeTCoeff.x;
                    param[1] =  t->unit[t->curTextureUnit].eyeTCoeff.y;
                    param[2] =  t->unit[t->curTextureUnit].eyeTCoeff.z;
                    param[3] =  t->unit[t->curTextureUnit].eyeTCoeff.w;
                    break;
                case GL_R:
                    param[0] =  t->unit[t->curTextureUnit].eyeRCoeff.x;
                    param[1] =  t->unit[t->curTextureUnit].eyeRCoeff.y;
                    param[2] =  t->unit[t->curTextureUnit].eyeRCoeff.z;
                    param[3] =  t->unit[t->curTextureUnit].eyeRCoeff.w;
                    break;
                case GL_Q:
                    param[0] =  t->unit[t->curTextureUnit].eyeQCoeff.x;
                    param[1] =  t->unit[t->curTextureUnit].eyeQCoeff.y;
                    param[2] =  t->unit[t->curTextureUnit].eyeQCoeff.z;
                    param[3] =  t->unit[t->curTextureUnit].eyeQCoeff.w;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGenfv called with bogus coord: %d", coord);
                    return;
            }
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glGetTexGenfv called with bogus pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateGetTexGeniv(GLenum coord, GLenum pname, GLint *param)
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                    "glGetTexGeniv called in begin/end");
        return;
    }

    switch (pname) {
        case GL_TEXTURE_GEN_MODE:
            switch (coord) {
                case GL_S:
                    *param = (GLint) t->unit[t->curTextureUnit].gen.s;
                    break;
                case GL_T:
                    *param = (GLint) t->unit[t->curTextureUnit].gen.t;
                    break;
                case GL_R:
                    *param = (GLint) t->unit[t->curTextureUnit].gen.r;
                    break;
                case GL_Q:
                    *param = (GLint) t->unit[t->curTextureUnit].gen.q;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGeniv called with bogus coord: %d", coord);
                    return;
            }
            break;
        case GL_OBJECT_PLANE:
            switch (coord) {
                case GL_S:
                    param[0] = (GLint) t->unit[t->curTextureUnit].objSCoeff.x;
                    param[1] = (GLint) t->unit[t->curTextureUnit].objSCoeff.y;
                    param[2] = (GLint) t->unit[t->curTextureUnit].objSCoeff.z;
                    param[3] = (GLint) t->unit[t->curTextureUnit].objSCoeff.w;
                    break;
                case GL_T:
                    param[0] = (GLint) t->unit[t->curTextureUnit].objTCoeff.x;
                    param[1] = (GLint) t->unit[t->curTextureUnit].objTCoeff.y;
                    param[2] = (GLint) t->unit[t->curTextureUnit].objTCoeff.z;
                    param[3] = (GLint) t->unit[t->curTextureUnit].objTCoeff.w;
                    break;
                case GL_R:
                    param[0] = (GLint) t->unit[t->curTextureUnit].objRCoeff.x;
                    param[1] = (GLint) t->unit[t->curTextureUnit].objRCoeff.y;
                    param[2] = (GLint) t->unit[t->curTextureUnit].objRCoeff.z;
                    param[3] = (GLint) t->unit[t->curTextureUnit].objRCoeff.w;
                    break;
                case GL_Q:
                    param[0] = (GLint) t->unit[t->curTextureUnit].objQCoeff.x;
                    param[1] = (GLint) t->unit[t->curTextureUnit].objQCoeff.y;
                    param[2] = (GLint) t->unit[t->curTextureUnit].objQCoeff.z;
                    param[3] = (GLint) t->unit[t->curTextureUnit].objQCoeff.w;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGeniv called with bogus coord: %d", coord);
                    return;
            }
            break;
        case GL_EYE_PLANE:
            switch (coord) {
                case GL_S:
                    param[0] = (GLint) t->unit[t->curTextureUnit].eyeSCoeff.x;
                    param[1] = (GLint) t->unit[t->curTextureUnit].eyeSCoeff.y;
                    param[2] = (GLint) t->unit[t->curTextureUnit].eyeSCoeff.z;
                    param[3] = (GLint) t->unit[t->curTextureUnit].eyeSCoeff.w;
                    break;
                case GL_T:
                    param[0] = (GLint) t->unit[t->curTextureUnit].eyeTCoeff.x;
                    param[1] = (GLint) t->unit[t->curTextureUnit].eyeTCoeff.y;
                    param[2] = (GLint) t->unit[t->curTextureUnit].eyeTCoeff.z;
                    param[3] = (GLint) t->unit[t->curTextureUnit].eyeTCoeff.w;
                    break;
                case GL_R:
                    param[0] = (GLint) t->unit[t->curTextureUnit].eyeRCoeff.x;
                    param[1] = (GLint) t->unit[t->curTextureUnit].eyeRCoeff.y;
                    param[2] = (GLint) t->unit[t->curTextureUnit].eyeRCoeff.z;
                    param[3] = (GLint) t->unit[t->curTextureUnit].eyeRCoeff.w;
                    break;
                case GL_Q:
                    param[0] = (GLint) t->unit[t->curTextureUnit].eyeQCoeff.x;
                    param[1] = (GLint) t->unit[t->curTextureUnit].eyeQCoeff.y;
                    param[2] = (GLint) t->unit[t->curTextureUnit].eyeQCoeff.z;
                    param[3] = (GLint) t->unit[t->curTextureUnit].eyeQCoeff.w;
                    break;
                default:
                    crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                                "glGetTexGeniv called with bogus coord: %d", coord);
                    return;
            }
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                        "glGetTexGen called with bogus pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateGetTexLevelParameterfv(GLenum target, GLint level, 
                                                            GLenum pname, GLfloat *params) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);
    CRTextureObj *tobj;
    CRTextureLevel *timg;

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
            "glGetTexLevelParameterfv called in begin/end");
        return;
    }

    if (level < 0 && level > t->maxLevel)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
            "glGetTexLevelParameterfv: Invalid level: %d", level);
        return;
    }

    crStateGetTextureObjectAndImage(g, target, level, &tobj, &timg);
    if (!timg)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
            "GetTexLevelParameterfv: invalid target: 0x%x or level %d",
            target, level);
        return;
    }

    switch (pname) 
    {
        case GL_TEXTURE_WIDTH:
            *params = (GLfloat) timg->width;
            break;
        case GL_TEXTURE_HEIGHT:
            *params = (GLfloat) timg->height;
            break;
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_DEPTH:
            *params = (GLfloat) timg->depth;
            break;
#endif
        case GL_TEXTURE_INTERNAL_FORMAT:
            *params = (GLfloat) timg->internalFormat;
            break;
        case GL_TEXTURE_BORDER:
            *params = (GLfloat) timg->border;
            break;
        case GL_TEXTURE_RED_SIZE:
            *params = (GLfloat) timg->texFormat->redbits;
            break;
        case GL_TEXTURE_GREEN_SIZE:
            *params = (GLfloat) timg->texFormat->greenbits;
            break;
        case GL_TEXTURE_BLUE_SIZE:
            *params = (GLfloat) timg->texFormat->bluebits;
            break;
        case GL_TEXTURE_ALPHA_SIZE:
            *params = (GLfloat) timg->texFormat->alphabits;
            break;
        case GL_TEXTURE_INTENSITY_SIZE:
            *params = (GLfloat) timg->texFormat->intensitybits;
            break;
        case GL_TEXTURE_LUMINANCE_SIZE:
            *params = (GLfloat) timg->texFormat->luminancebits;
            break;
#if CR_ARB_texture_compression
        case GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB:
             *params = (GLfloat) timg->bytes;
             break;
        case GL_TEXTURE_COMPRESSED_ARB:
             *params = (GLfloat) timg->compressed;
             break;
#endif
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                "GetTexLevelParameterfv: invalid pname: 0x%x",
                pname);
            return;
    }
}


void STATE_APIENTRY
crStateGetTexLevelParameteriv(GLenum target, GLint level, 
                                                            GLenum pname, GLint *params) 
{
    CRContext *g = GetCurrentContext();
    CRTextureState *t = &(g->texture);
    CRTextureObj *tobj;
    CRTextureLevel *timg;

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
            "glGetTexLevelParameteriv called in begin/end");
        return;
    }

    if (level < 0 && level > t->maxLevel)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_VALUE,
            "glGetTexLevelParameteriv: Invalid level: %d", level);
        return;
    }

    crStateGetTextureObjectAndImage(g, target, level, &tobj, &timg);
    if (!timg)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
            "GetTexLevelParameteriv: invalid target: 0x%x",
            target);
        return;
    }

    switch (pname) 
    {
        case GL_TEXTURE_WIDTH:
            *params = (GLint) timg->width;
            break;
        case GL_TEXTURE_HEIGHT:
            *params = (GLint) timg->height;
            break;
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_DEPTH:
            *params = (GLint) timg->depth;
            break;
#endif
        case GL_TEXTURE_INTERNAL_FORMAT:
            *params = (GLint) timg->internalFormat;
            break;
        case GL_TEXTURE_BORDER:
            *params = (GLint) timg->border;
            break;
        case GL_TEXTURE_RED_SIZE:
            *params = (GLint) timg->texFormat->redbits;
            break;
        case GL_TEXTURE_GREEN_SIZE:
            *params = (GLint) timg->texFormat->greenbits;
            break;
        case GL_TEXTURE_BLUE_SIZE:
            *params = (GLint) timg->texFormat->bluebits;
            break;
        case GL_TEXTURE_ALPHA_SIZE:
            *params = (GLint) timg->texFormat->alphabits;
            break;
        case GL_TEXTURE_INTENSITY_SIZE:
            *params = (GLint) timg->texFormat->intensitybits;
            break;
        case GL_TEXTURE_LUMINANCE_SIZE:
            *params = (GLint) timg->texFormat->luminancebits;
            break;

#if 0
        /* XXX TODO */
        case GL_TEXTURE_DEPTH_SIZE:
            *params = (GLint) timg->texFormat->depthSize;
            break;
#endif
#if CR_ARB_texture_compression
        case GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB:
            *params = (GLint) timg->bytes;
            break;
        case GL_TEXTURE_COMPRESSED_ARB:
            *params = (GLint) timg->compressed;
            break;
#endif
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                "GetTexLevelParameteriv: invalid pname: 0x%x",
                pname);
            return;
    }
}


void STATE_APIENTRY
crStateGetTexParameterfv(GLenum target, GLenum pname, GLfloat *params) 
{
    CRContext *g = GetCurrentContext();
    CRTextureObj *tobj;
    CRTextureLevel *tl;

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                    "glGetTexParameterfv called in begin/end");
        return;
    }

    crStateGetTextureObjectAndImage(g, target, 0, &tobj, &tl);
    if (!tobj)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
            "glGetTexParameterfv: invalid target: 0x%x", target);
        return;
    }

    switch (pname) 
    {
        case GL_TEXTURE_MAG_FILTER:
            *params = (GLfloat) tobj->magFilter;
            break;
        case GL_TEXTURE_MIN_FILTER:
            *params = (GLfloat) tobj->minFilter;
            break;
        case GL_TEXTURE_WRAP_S:
            *params = (GLfloat) tobj->wrapS;
            break;
        case GL_TEXTURE_WRAP_T:
            *params = (GLfloat) tobj->wrapT;
            break;
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_WRAP_R:
            *params = (GLfloat) tobj->wrapR;
            break;
        case GL_TEXTURE_PRIORITY:
            *params = (GLfloat) tobj->priority;
            break;
#endif
        case GL_TEXTURE_BORDER_COLOR:
            params[0] = tobj->borderColor.r;
            params[1] = tobj->borderColor.g;
            params[2] = tobj->borderColor.b;
            params[3] = tobj->borderColor.a;
            break;
#ifdef CR_EXT_texture_filter_anisotropic
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            if (g->extensions.EXT_texture_filter_anisotropic) {
                *params = (GLfloat) tobj->maxAnisotropy;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameterfv: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_ARB_depth_texture
        case GL_DEPTH_TEXTURE_MODE_ARB:
            if (g->extensions.ARB_depth_texture) {
                *params = (GLfloat) tobj->depthMode;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_ARB_shadow
        case GL_TEXTURE_COMPARE_MODE_ARB:
            if (g->extensions.ARB_shadow) {
                *params = (GLfloat) tobj->compareMode;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
        case GL_TEXTURE_COMPARE_FUNC_ARB:
            if (g->extensions.ARB_shadow) {
                *params = (GLfloat) tobj->compareFunc;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_ARB_shadow_ambient
        case GL_TEXTURE_COMPARE_FAIL_VALUE_ARB:
            if (g->extensions.ARB_shadow_ambient) {
                *params = (GLfloat) tobj->compareFailValue;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_SGIS_generate_mipmap
        case GL_GENERATE_MIPMAP_SGIS:
            if (g->extensions.SGIS_generate_mipmap) {
                *params = (GLfloat) tobj->generateMipmap;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_MIN_LOD:
            *params = (GLfloat) tobj->minLod;
            break;
        case GL_TEXTURE_MAX_LOD:
            *params = (GLfloat) tobj->maxLod;
            break;
        case GL_TEXTURE_BASE_LEVEL:
            *params = (GLfloat) tobj->baseLevel;
            break;
        case GL_TEXTURE_MAX_LEVEL:
            *params = (GLfloat) tobj->maxLevel;
            break;
#endif
#if 0
        case GL_TEXTURE_LOD_BIAS_EXT:
            /* XXX todo */
            *params = (GLfloat) tobj->lodBias;
            break;
#endif
            case GL_TEXTURE_RESIDENT:
            /* XXX todo */
            crWarning("glGetTexParameterfv GL_TEXTURE_RESIDENT is unimplemented");
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                "glGetTexParameterfv: invalid pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStateGetTexParameteriv(GLenum target, GLenum pname, GLint *params) 
{
    CRContext *g = GetCurrentContext();
    CRTextureObj *tobj;
    CRTextureLevel *tl;

    if (g->current.inBeginEnd)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_OPERATION,
                    "glGetTexParameter called in begin/end");
        return;
    }

    crStateGetTextureObjectAndImage(g, target, 0, &tobj, &tl);
    if (!tobj)
    {
        crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
            "glGetTexParameteriv: invalid target: 0x%x", target);
        return;
    }

    switch (pname) 
    {
        case GL_TEXTURE_MAG_FILTER:
            *params = (GLint) tobj->magFilter;
            break;
        case GL_TEXTURE_MIN_FILTER:
            *params = (GLint) tobj->minFilter;
            break;
        case GL_TEXTURE_WRAP_S:
            *params = (GLint) tobj->wrapS;
            break;
        case GL_TEXTURE_WRAP_T:
            *params = (GLint) tobj->wrapT;
            break;
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_WRAP_R:
            *params = (GLint) tobj->wrapR;
            break;
        case GL_TEXTURE_PRIORITY:
            *params = (GLint) tobj->priority;
            break;
#endif
        case GL_TEXTURE_BORDER_COLOR:
            params[0] = (GLint) (tobj->borderColor.r * CR_MAXINT);
            params[1] = (GLint) (tobj->borderColor.g * CR_MAXINT);
            params[2] = (GLint) (tobj->borderColor.b * CR_MAXINT);
            params[3] = (GLint) (tobj->borderColor.a * CR_MAXINT);
            break;
#ifdef CR_OPENGL_VERSION_1_2
        case GL_TEXTURE_MIN_LOD:
            *params = (GLint) tobj->minLod;
            break;
        case GL_TEXTURE_MAX_LOD:
            *params = (GLint) tobj->maxLod;
            break;
        case GL_TEXTURE_BASE_LEVEL:
            *params = (GLint) tobj->baseLevel;
            break;
        case GL_TEXTURE_MAX_LEVEL:
            *params = (GLint) tobj->maxLevel;
            break;
#endif
#ifdef CR_EXT_texture_filter_anisotropic
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            if (g->extensions.EXT_texture_filter_anisotropic) {
                *params = (GLint) tobj->maxAnisotropy;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, 
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_ARB_depth_texture
        case GL_DEPTH_TEXTURE_MODE_ARB:
            if (g->extensions.ARB_depth_texture) {
                *params = (GLint) tobj->depthMode;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_ARB_shadow
        case GL_TEXTURE_COMPARE_MODE_ARB:
            if (g->extensions.ARB_shadow) {
                *params = (GLint) tobj->compareMode;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
        case GL_TEXTURE_COMPARE_FUNC_ARB:
            if (g->extensions.ARB_shadow) {
                *params = (GLint) tobj->compareFunc;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_ARB_shadow_ambient
        case GL_TEXTURE_COMPARE_FAIL_VALUE_ARB:
            if (g->extensions.ARB_shadow_ambient) {
                *params = (GLint) tobj->compareFailValue;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
#ifdef CR_SGIS_generate_mipmap
        case GL_GENERATE_MIPMAP_SGIS:
            if (g->extensions.SGIS_generate_mipmap) {
                *params = (GLint) tobj->generateMipmap;
            }
            else {
                crStateError(__LINE__, __FILE__, GL_INVALID_ENUM,
                    "glGetTexParameter: invalid pname: 0x%x", pname);
                return;
            }
            break;
#endif
        case GL_TEXTURE_RESIDENT:
            /* XXX todo */
            crWarning("glGetTexParameteriv GL_TEXTURE_RESIDENT is unimplemented");
            break;
        default:
            crStateError(__LINE__, __FILE__, GL_INVALID_ENUM, 
                "glGetTexParameter: invalid pname: %d", pname);
            return;
    }
}


void STATE_APIENTRY
crStatePrioritizeTextures(GLsizei n, const GLuint *textures,
                                                    const GLclampf *priorities) 
{
    UNUSED(n);
    UNUSED(textures);
    UNUSED(priorities);
    /* TODO: */
    return;
}


GLboolean STATE_APIENTRY
crStateAreTexturesResident(GLsizei n, const GLuint *textures,
                                                     GLboolean *residences) 
{
    UNUSED(n);
    UNUSED(textures);
    UNUSED(residences);
    /* TODO: */
    return GL_TRUE;
}


GLboolean STATE_APIENTRY
crStateIsTexture(GLuint texture)
{
    CRContext *g = GetCurrentContext();
    CRTextureObj *tobj;

    GET_TOBJ(tobj, g, texture);
    return tobj != NULL;
}

static void crStateCheckTextureHWIDCB(unsigned long key, void *data1, void *data2)
{
    CRTextureObj *pTex = (CRTextureObj *) data1;
    crCheckIDHWID_t *pParms = (crCheckIDHWID_t*) data2;
    (void) key;

    if (crStateGetTextureObjHWID(pTex)==pParms->hwid)
        pParms->id = pTex->id;
}

DECLEXPORT(GLuint) STATE_APIENTRY crStateTextureHWIDtoID(GLuint hwid)
{
    CRContext *g = GetCurrentContext();
    crCheckIDHWID_t parms;

    parms.id = hwid;
    parms.hwid = hwid;

    crHashtableWalk(g->shared->textureTable, crStateCheckTextureHWIDCB, &parms);
    return parms.id;
}

DECLEXPORT(GLuint) STATE_APIENTRY crStateGetTextureHWID(GLuint id)
{
    CRContext *g = GetCurrentContext();
    CRTextureObj *tobj = GET_TOBJ(tobj, g, id);

#ifdef DEBUG_misha
    if (id)
    {
        Assert(tobj);
    }
    else
    {
        Assert(!tobj);
    }
    if (tobj)
    {
        crDebug("tex id(%d), hwid(%d)", tobj->id, tobj->hwid);
    }
#endif


    return tobj ? crStateGetTextureObjHWID(tobj) : 0;
}

DECLEXPORT(GLuint) STATE_APIENTRY crStateGetTextureObjHWID(CRTextureObj *tobj)
{
    CRASSERT(tobj);

#ifndef IN_GUEST
    if (tobj->id && !tobj->hwid)
    {
        CRASSERT(diff_api.GenTextures);
        diff_api.GenTextures(1, &tobj->hwid);
#ifdef DEBUG_misha
        crDebug("tex id(%d), hwid(%d)", tobj->id, tobj->hwid);
#endif
        CRASSERT(tobj->hwid);
    }
#endif

    return tobj->hwid;
}
