text:
/* $Id: state_snapshot.c $ */

/** @file
 * VBox Context state saving/loading used by VM snapshot
 */

/*
 * Copyright (C) 2008-2013 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */

#include "state.h"
#include "state_internals.h"
#include "state/cr_statetypes.h"
#include "state/cr_texture.h"
#include "cr_mem.h"
#include "cr_string.h"
#include "cr_pixeldata.h"
#include <stdio.h>

#include <iprt/assert.h>
#include <iprt/types.h>
#include <iprt/err.h>
#include <VBox/err.h>

/* @todo
 * We have two ways of saving/loading states.
 *
 * First which is being used atm, just pure saving/loading of structures.
 * The drawback is we have to deal with all the pointers around those structures,
 * we'd have to update this code if we'd change state tracking.
 * On the bright side it's fast, though it's not really needed as it's not that often operation.
 * It could also worth to split those functions into appropriate parts,
 * similar to the way context creation is being done.
 *
 * Second way would be to implement full dispatch api table and substitute diff_api during saving/loading.
 * Then if we implement that api in a similar way to packer/unpacker with a change to store/load
 * via provided pSSM handle instead of pack buffer,
 * saving state could be done by simple diffing against empty "dummy" context.
 * Restoring state in such case would look like unpacking commands from pSSM instead of network buffer.
 * This would be slower (who cares) but most likely will not require any code changes to support in future.
 * We will reduce amount of saved data as we'd save only changed state parts, but I doubt it'd be that much.
 * It could be done for the first way as well, but requires tons of bit checks.
 */

static int32_t crStateAllocAndSSMR3GetMem(PSSMHANDLE pSSM, void **pBuffer, size_t cbBuffer)
{
    CRASSERT(pSSM && pBuffer && cbBuffer>0);

    *pBuffer = crAlloc(cbBuffer);
    if (!*pBuffer)
        return VERR_NO_MEMORY;

    return SSMR3GetMem(pSSM, *pBuffer, cbBuffer);
}

#define SHCROGL_GET_STRUCT_PART(_pPtr, _type, _from, _to) do { \
            rc = SSMR3GetMem(pSSM, &(_pPtr)->_from, RT_OFFSETOF(_type, _to) - RT_OFFSETOF(_type, _from)); \
            AssertRCReturn(rc, rc); \
        } while (0)

#define SHCROGL_GET_STRUCT_TAIL(_pPtr, _type, _from) do { \
            rc = SSMR3GetMem(pSSM, &(_pPtr)->_from, sizeof (_type) - RT_OFFSETOF(_type, _from)); \
            AssertRCReturn(rc, rc); \
        } while (0)

#define SHCROGL_GET_STRUCT_HEAD(_pPtr, _type, _to) do { \
            rc = SSMR3GetMem(pSSM, (_pPtr), RT_OFFSETOF(_type, _to)); \
            AssertRCReturn(rc, rc); \
        } while (0)

#define SHCROGL_CUT_FIELD_ALIGNMENT_SIZE(_type, _prevField, _field) (RT_OFFSETOF(_type, _field) - RT_OFFSETOF(_type, _prevField) - RT_SIZEOFMEMB(_type, _prevField))
#define SHCROGL_CUT_FIELD_ALIGNMENT(_type, _prevField, _field) do { \
            const int32_t cbAlignment = SHCROGL_CUT_FIELD_ALIGNMENT_SIZE(_type, _prevField, _field) ; \
            /*AssertCompile(SHCROGL_CUT_FIELD_ALIGNMENT_SIZE(_type, _prevField, _field) >= 0 && SHCROGL_CUT_FIELD_ALIGNMENT_SIZE(_type, _prevField, _field) < sizeof (void*));*/ \
            if (cbAlignment) { \
                rc = SSMR3Skip(pSSM, cbAlignment); \
            } \
        } while (0)

#define SHCROGL_ROUNDBOUND(_v, _b) (((_v) + ((_b) - 1)) & ~((_b) - 1))
#define SHCROGL_ALIGNTAILSIZE(_v, _b) (SHCROGL_ROUNDBOUND((_v),(_b)) - (_v))
#define SHCROGL_CUT_FOR_OLD_TYPE_TO_ENSURE_ALIGNMENT_SIZE(_type, _field, _oldFieldType, _nextFieldAllignment) (SHCROGL_ALIGNTAILSIZE(((RT_OFFSETOF(_type, _field) + sizeof (_oldFieldType))), (_nextFieldAllignment)))
#define SHCROGL_CUT_FOR_OLD_TYPE_TO_ENSURE_ALIGNMENT(_type, _field, _oldFieldType, _nextFieldAllignment)  do { \
        const int32_t cbAlignment = SHCROGL_CUT_FOR_OLD_TYPE_TO_ENSURE_ALIGNMENT_SIZE(_type, _field, _oldFieldType, _nextFieldAllignment); \
        /*AssertCompile(SHCROGL_CUT_TAIL_ALIGNMENT_SIZE(_type, _lastField) >= 0 && SHCROGL_CUT_TAIL_ALIGNMENT_SIZE(_type, _lastField) < sizeof (void*));*/ \
        if (cbAlignment) { \
            rc = SSMR3Skip(pSSM, cbAlignment); \
        } \
    } while (0)

#define SHCROGL_CUT_TAIL_ALIGNMENT_SIZE(_type, _lastField) (sizeof (_type) - RT_OFFSETOF(_type, _lastField) - RT_SIZEOFMEMB(_type, _lastField))
#define SHCROGL_CUT_TAIL_ALIGNMENT(_type, _lastField) do { \
            const int32_t cbAlignment = SHCROGL_CUT_TAIL_ALIGNMENT_SIZE(_type, _lastField); \
            /*AssertCompile(SHCROGL_CUT_TAIL_ALIGNMENT_SIZE(_type, _lastField) >= 0 && SHCROGL_CUT_TAIL_ALIGNMENT_SIZE(_type, _lastField) < sizeof (void*));*/ \
            if (cbAlignment) { \
                rc = SSMR3Skip(pSSM, cbAlignment); \
            } \
        } while (0)

static int32_t crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(CRTextureObj *pTexture, PSSMHANDLE pSSM)
{
    int32_t rc;
    uint32_t cbObj = RT_OFFSETOF(CRTextureObj, ctxUsage);
    cbObj = ((cbObj + sizeof (void*) - 1) & ~(sizeof (void*) - 1));
    rc = SSMR3GetMem(pSSM, pTexture, cbObj);
    AssertRCReturn(rc, rc);
    /* just make all bits are used so that we fall back to the pre-ctxUsage behavior,
     * i.e. all shared resources will be destructed on last shared context termination */
    FILLDIRTY(pTexture->ctxUsage);
    return rc;
}

static int32_t crStateLoadTextureUnit_v_BEFORE_CTXUSAGE_BITS(CRTextureUnit *t, PSSMHANDLE pSSM)
{
    int32_t rc;
    SHCROGL_GET_STRUCT_HEAD(t, CRTextureUnit, Saved1D);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->Saved1D, pSSM);
    AssertRCReturn(rc, rc);
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureUnit, Saved1D, Saved2D);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->Saved2D, pSSM);
    AssertRCReturn(rc, rc);
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureUnit, Saved2D, Saved3D);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->Saved3D, pSSM);
    AssertRCReturn(rc, rc);
#ifdef CR_ARB_texture_cube_map
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureUnit, Saved3D, SavedCubeMap);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->SavedCubeMap, pSSM);
    AssertRCReturn(rc, rc);
# define SHCROGL_INTERNAL_LAST_FIELD SavedCubeMap
#else
# define SHCROGL_INTERNAL_LAST_FIELD Saved3D
#endif
#ifdef CR_NV_texture_rectangle
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureUnit, SHCROGL_INTERNAL_LAST_FIELD, SavedRect);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->SavedRect, pSSM);
    AssertRCReturn(rc, rc);
# undef SHCROGL_INTERNAL_LAST_FIELD
# define SHCROGL_INTERNAL_LAST_FIELD SavedRect
#endif
    SHCROGL_CUT_TAIL_ALIGNMENT(CRTextureUnit, SHCROGL_INTERNAL_LAST_FIELD);
#undef SHCROGL_INTERNAL_LAST_FIELD
    return rc;
}

static int crStateLoadStencilPoint_v_37(CRPointState *pPoint, PSSMHANDLE pSSM)
{
    int rc = VINF_SUCCESS;
    SHCROGL_GET_STRUCT_HEAD(pPoint, CRPointState, spriteCoordOrigin);
    pPoint->spriteCoordOrigin = (GLfloat)GL_UPPER_LEFT;
    return rc;
}

static int32_t crStateLoadStencilState_v_33(CRStencilState *s, PSSMHANDLE pSSM)
{
    CRStencilState_v_33 stencilV33;
    int32_t rc = SSMR3GetMem(pSSM, &stencilV33, sizeof (stencilV33));
    AssertRCReturn(rc, rc);
    s->stencilTest = stencilV33.stencilTest;
    s->stencilTwoSideEXT = GL_FALSE;
    s->activeStencilFace = GL_FRONT;
    s->clearValue = stencilV33.clearValue;
    s->writeMask = stencilV33.writeMask;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].func = stencilV33.func;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].mask = stencilV33.mask;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].ref = stencilV33.ref;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].fail = stencilV33.fail;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].passDepthFail = stencilV33.passDepthFail;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].passDepthPass = stencilV33.passDepthPass;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_BACK] = s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT];
    crStateStencilBufferInit(&s->buffers[CRSTATE_STENCIL_BUFFER_ID_TWO_SIDE_BACK]);
    return VINF_SUCCESS;
}

static int32_t crStateLoadTextureState_v_BEFORE_CTXUSAGE_BITS(CRTextureState *t, PSSMHANDLE pSSM)
{
    GLint i;
    int32_t rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->base1D, pSSM);
    AssertRCReturn(rc, rc);
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, base1D, base2D);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->base2D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->base3D, pSSM);
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, base2D, base3D);
    AssertRCReturn(rc, rc);
#ifdef CR_ARB_texture_cube_map
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, base3D, baseCubeMap);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->baseCubeMap, pSSM);
    AssertRCReturn(rc, rc);
# define SHCROGL_INTERNAL_LAST_FIELD baseCubeMap
#else
# define SHCROGL_INTERNAL_LAST_FIELD base3D
#endif
#ifdef CR_NV_texture_rectangle
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, SHCROGL_INTERNAL_LAST_FIELD, baseRect);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->baseRect, pSSM);
    AssertRCReturn(rc, rc);
# undef SHCROGL_INTERNAL_LAST_FIELD
# define SHCROGL_INTERNAL_LAST_FIELD baseRect
#endif
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, SHCROGL_INTERNAL_LAST_FIELD, proxy1D);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->proxy1D, pSSM);
    AssertRCReturn(rc, rc);
#undef SHCROGL_INTERNAL_LAST_FIELD
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, proxy1D, proxy2D);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->proxy2D, pSSM);
    AssertRCReturn(rc, rc);
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, proxy2D, proxy3D);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->proxy3D, pSSM);
    AssertRCReturn(rc, rc);
#ifdef CR_ARB_texture_cube_map
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, proxy3D, proxyCubeMap);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->proxyCubeMap, pSSM);
    AssertRCReturn(rc, rc);
# define SHCROGL_INTERNAL_LAST_FIELD proxyCubeMap
#else
# define SHCROGL_INTERNAL_LAST_FIELD proxy3D
#endif
#ifdef CR_NV_texture_rectangle
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, SHCROGL_INTERNAL_LAST_FIELD, proxyRect);
    rc = crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(&t->proxyRect, pSSM);
    AssertRCReturn(rc, rc);
# undef SHCROGL_INTERNAL_LAST_FIELD
# define SHCROGL_INTERNAL_LAST_FIELD proxyRect
#endif
    SHCROGL_CUT_FIELD_ALIGNMENT(CRTextureState, SHCROGL_INTERNAL_LAST_FIELD, curTextureUnit);
# undef SHCROGL_INTERNAL_LAST_FIELD
    SHCROGL_GET_STRUCT_PART(t, CRTextureState, curTextureUnit, unit);

    for (i = 0; i < CR_MAX_TEXTURE_UNITS; ++i)
    {
        rc = crStateLoadTextureUnit_v_BEFORE_CTXUSAGE_BITS(&t->unit[i], pSSM);
        AssertRCReturn(rc, rc);
    }

    SHCROGL_CUT_TAIL_ALIGNMENT(CRTextureState, unit);

    return VINF_SUCCESS;
}

static int32_t crStateStencilBufferStack_v_33(CRStencilBufferStack *s, PSSMHANDLE pSSM)
{
    CRStencilBufferStack_v_33 stackV33;
    int32_t rc = SSMR3GetMem(pSSM, &stackV33, sizeof (stackV33));

    s->stencilTest = stackV33.stencilTest;
    s->stencilTwoSideEXT = GL_FALSE;
    s->activeStencilFace = GL_FRONT;
    s->clearValue = stackV33.clearValue;
    s->writeMask = stackV33.writeMask;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].func = stackV33.func;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].mask = stackV33.mask;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].ref = stackV33.ref;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].fail = stackV33.fail;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].passDepthFail = stackV33.passDepthFail;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT].passDepthPass = stackV33.passDepthPass;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_BACK] = s->buffers[CRSTATE_STENCIL_BUFFER_ID_FRONT];

    s->buffers[CRSTATE_STENCIL_BUFFER_ID_TWO_SIDE_BACK].func = GL_ALWAYS;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_TWO_SIDE_BACK].mask = 0xFFFFFFFF;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_TWO_SIDE_BACK].ref = 0;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_TWO_SIDE_BACK].fail = GL_KEEP;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_TWO_SIDE_BACK].passDepthFail = GL_KEEP;
    s->buffers[CRSTATE_STENCIL_BUFFER_ID_TWO_SIDE_BACK].passDepthPass = GL_KEEP;

    return VINF_SUCCESS;
}

static int32_t crStateLoadAttribState_v_33(CRAttribState *t, PSSMHANDLE pSSM)
{
    int32_t i, rc;
    SHCROGL_GET_STRUCT_HEAD(t, CRAttribState, stencilBufferStack);
    for (i = 0; i < CR_MAX_ATTRIB_STACK_DEPTH; ++i)
    {
        rc = crStateStencilBufferStack_v_33(&t->stencilBufferStack[i], pSSM);
        AssertRCReturn(rc, rc);
    }
    SHCROGL_GET_STRUCT_TAIL(t, CRAttribState, textureStackDepth);
    return rc;
}

static int32_t crStateLoadTextureStack_v_BEFORE_CTXUSAGE_BITS(CRTextureStack *t, PSSMHANDLE pSSM)
{
    int32_t i, rc;
    SHCROGL_GET_STRUCT_HEAD(t, CRTextureStack, unit);
    for (i = 0; i < CR_MAX_TEXTURE_UNITS; ++i)
    {
        rc = crStateLoadTextureUnit_v_BEFORE_CTXUSAGE_BITS(&t->unit[i], pSSM);
        AssertRCReturn(rc, rc);
    }
    SHCROGL_CUT_TAIL_ALIGNMENT(CRTextureStack, unit);
    return rc;
}

static int32_t crStateLoadAttribState_v_BEFORE_CTXUSAGE_BITS(CRAttribState *t, PSSMHANDLE pSSM)
{
    int32_t i, rc;

    SHCROGL_GET_STRUCT_HEAD(t, CRAttribState, stencilBufferStack);
    for (i = 0; i < CR_MAX_ATTRIB_STACK_DEPTH; ++i)
    {
        rc = crStateStencilBufferStack_v_33(&t->stencilBufferStack[i], pSSM);
        AssertRCReturn(rc, rc);
    }
    SHCROGL_GET_STRUCT_PART(t, CRAttribState, textureStackDepth, textureStack);
    for (i = 0; i < CR_MAX_ATTRIB_STACK_DEPTH; ++i)
    {
        rc = crStateLoadTextureStack_v_BEFORE_CTXUSAGE_BITS(&t->textureStack[i], pSSM);
        AssertRCReturn(rc, rc);
    }
    SHCROGL_GET_STRUCT_TAIL(t, CRAttribState, transformStackDepth);
    return rc;
}

static int32_t crStateLoadTextureObj(CRTextureObj *pTexture, PSSMHANDLE pSSM, uint32_t u32Version)
{
    int32_t rc;
    if (u32Version == SHCROGL_SSM_VERSION_BEFORE_CTXUSAGE_BITS)
        return crStateLoadTextureObj_v_BEFORE_CTXUSAGE_BITS(pTexture, pSSM);
    rc = SSMR3GetMem(pSSM, pTexture, sizeof (*pTexture));
    AssertRCReturn(rc, rc);
    return rc;
}

static int32_t crStateLoadBufferObject(CRBufferObject *pBufferObj, PSSMHANDLE pSSM, uint32_t u32Version)
{
    int32_t rc;
    if (u32Version == SHCROGL_SSM_VERSION_BEFORE_CTXUSAGE_BITS)
    {
        uint32_t cbObj = RT_OFFSETOF(CRBufferObject, ctxUsage);
        cbObj = ((cbObj + sizeof (void*) - 1) & ~(sizeof (void*) - 1));
        rc = SSMR3GetMem(pSSM, pBufferObj, cbObj);
        AssertRCReturn(rc, rc);
        /* just make all bits are used so that we fall back to the pre-ctxUsage behavior,
         * i.e. all shared resources will be destructed on last shared context termination */
        FILLDIRTY(pBufferObj->ctxUsage);
    }
    else
    {
        rc = SSMR3GetMem(pSSM, pBufferObj, sizeof(*pBufferObj));
        AssertRCReturn(rc, rc);
    }
    return rc;
}

static int32_t crStateLoadFramebufferObject(CRFramebufferObject *pFBO, PSSMHANDLE pSSM, uint32_t u32Version)
{
    int32_t rc;
    if (u32Version == SHCROGL_SSM_VERSION_BEFORE_CTXUSAGE_BITS)
    {
        uint32_t cbObj = RT_OFFSETOF(CRFramebufferObject, ctxUsage);
        cbObj = ((cbObj + sizeof (void*) - 1) & ~(sizeof (void*) - 1));
        rc = SSMR3GetMem(pSSM, pFBO, cbObj);
        AssertRCReturn(rc, rc);
        /* just make all bits are used so that we fall back to the pre-ctxUsage behavior,
         * i.e. all shared resources will be destructed on last shared context termination */
        FILLDIRTY(pFBO->ctxUsage);
    }
    else
    {
        rc = SSMR3GetMem(pSSM, pFBO, sizeof(*pFBO));
        AssertRCReturn(rc, rc);
    }
    return rc;
}

static int32_t crStateLoadRenderbufferObject(CRRenderbufferObject *pRBO, PSSMHANDLE pSSM, uint32_t u32Version)
{
    int32_t rc;
    if (u32Version == SHCROGL_SSM_VERSION_BEFORE_CTXUSAGE_BITS)
    {
        uint32_t cbObj = RT_OFFSETOF(CRRenderbufferObject, ctxUsage);
        cbObj = ((cbObj + sizeof (void*) - 1) & ~(sizeof (void*) - 1));
        rc = SSMR3GetMem(pSSM, pRBO, cbObj);
        AssertRCReturn(rc, rc);
        /* just make all bits are used so that we fall back to the pre-ctxUsage behavior,
         * i.e. all shared resources will be destructed on last shared context termination */
        FILLDIRTY(pRBO->ctxUsage);
    }
    else
    {
        rc = SSMR3GetMem(pSSM, pRBO, sizeof(*pRBO));
        AssertRCReturn(rc, rc);
    }
    return rc;
}

static int32_t crStateSaveTextureObjData(CRTextureObj *pTexture, PSSMHANDLE pSSM)
{
    int32_t rc, face, i;
    GLint bound = 0;

    CRASSERT(pTexture && pSSM);

    crDebug("crStateSaveTextureObjData %u. START", pTexture->id);

    for (face = 0; face < 6; face++) {
        CRASSERT(pTexture->level[face]);

        for (i = 0; i < CR_MAX_MIPMAP_LEVELS; i++) {
            CRTextureLevel *ptl = &(pTexture->level[face][i]);
            rc = SSMR3PutMem(pSSM, ptl, sizeof(*ptl));
            AssertRCReturn(rc, rc);
            if (ptl->img)
            {
                CRASSERT(ptl->bytes);
                rc = SSMR3PutMem(pSSM, ptl->img, ptl->bytes);
                AssertRCReturn(rc, rc);
            }
#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
            /* Note, this is not a bug.
             * Even with CR_STATE_NO_TEXTURE_IMAGE_STORE defined, it's possible that ptl->img!=NULL.
             * For ex. we're saving snapshot right after it was loaded
             * and some context hasn't been used by the guest application yet
             * (pContext->shared->bTexResyncNeeded==GL_TRUE).
             */
            else if (ptl->bytes)
            {
                char *pImg;
                GLenum target;

                if (!bound)
                {
                    GLenum getEnum;
                    diff_api.BindTexture(pTexture->target, pTexture->hwid);
                    bound = 1;

                    /* osx nvidia drivers seem to have a bug that 1x1 TEXTURE_2D texture becmes inaccessible for some reason
                     * saw that for 1x1 dummy textures generated by wine
                     * to avoid crashes we skip texture data save if that is the case */
                    switch (pTexture->target)
                    {
                        case GL_TEXTURE_1D:
                            getEnum = GL_TEXTURE_BINDING_1D;
                            break;
                        case GL_TEXTURE_2D:
                            getEnum = GL_TEXTURE_BINDING_2D;
                            break;
                        case GL_TEXTURE_3D:
                            getEnum = GL_TEXTURE_BINDING_3D;
                            break;
                        case GL_TEXTURE_RECTANGLE_ARB:
                            getEnum = GL_TEXTURE_BINDING_RECTANGLE_ARB;
                            break;
                        case GL_TEXTURE_CUBE_MAP_ARB:
                            getEnum = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
                            break;
                        default:
                            crWarning("unknown texture target: 0x%x", pTexture->target);
                            getEnum = 0;
                            break;
                    }

                    if (getEnum)
                    {
                        GLint curTex;
                        diff_api.GetIntegerv(getEnum, &curTex);
                        if (curTex != pTexture->hwid)
                        {
                            crWarning("texture not bound properly: expected %d, but was %d. Texture state data: target(0x%x), id(%d), w(%d), h(%d)",
                                    pTexture->hwid, curTex,
                                    pTexture->target,
                                    pTexture->id,
                                    ptl->width,
                                    ptl->height);
                            bound = -1;
                        }
                    }

                }

                if (pTexture->target!=GL_TEXTURE_CUBE_MAP_ARB)
                {
                    target = pTexture->target;
                }
                else
                {
                    target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
                }

#ifdef DEBUG
                pImg = crAlloc(ptl->bytes+4);
#else
                pImg = crAlloc(ptl->bytes);
#endif
                if (!pImg) return VERR_NO_MEMORY;

#ifdef DEBUG
                *(int*)((char*)pImg+ptl->bytes) = 0xDEADDEAD;
#endif
                if (bound > 0)
                {
#ifdef DEBUG
                    {
                        GLint w,h=0;
                        crDebug("get image: compressed %i, face %i, level %i, width %i, height %i, bytes %i",
                                ptl->compressed, face, i, ptl->width, ptl->height, ptl->bytes);
                        diff_api.GetTexLevelParameteriv(target, i, GL_TEXTURE_WIDTH, &w);
                        diff_api.GetTexLevelParameteriv(target, i, GL_TEXTURE_HEIGHT, &h);
                        if (w!=ptl->width || h!=ptl->height)
                        {
                            crWarning("!!!tex size mismatch %i, %i!!!", w, h);
                        }
                    }
#endif

                    /*@todo: ugly workaround for crashes inside ati driver,
                     *       they overwrite their own allocated memory in cases where texlevel >=4
                             and width or height <=2.
                     */
                    if (i<4 || (ptl->width>2 && ptl->height>2))
                    {
                        if (!ptl->compressed)
                        {
                            diff_api.GetTexImage(target, i, ptl->format, ptl->type, pImg);
                        }
                        else
                        {
                            diff_api.GetCompressedTexImageARB(target, i, pImg);
                        }
                    }
                }
                else
                {
                    crMemset(pImg, 0, ptl->bytes);
                }

#ifdef DEBUG
                if (*(int*)((char*)pImg+ptl->bytes) != 0xDEADDEAD)
                {
                    crWarning("Texture is bigger than expected!!!");
                }
#endif

                rc = SSMR3PutMem(pSSM, pImg, ptl->bytes);
                crFree(pImg);
                AssertRCReturn(rc, rc);
            }
#endif
        }
    }

    crDebug("crStateSaveTextureObjData %u. END", pTexture->id);

    return VINF_SUCCESS;
}

static int32_t crStateLoadTextureObjData(CRTextureObj *pTexture, PSSMHANDLE pSSM)
{
    int32_t rc, face, i;

    CRASSERT(pTexture && pSSM);

    for (face = 0; face < 6; face++) {
        CRASSERT(pTexture->level[face]);

        for (i = 0; i < CR_MAX_MIPMAP_LEVELS; i++) {
            CRTextureLevel *ptl = &(pTexture->level[face][i]);
            CRASSERT(!ptl->img);

            rc = SSMR3GetMem(pSSM, ptl, sizeof(*ptl));
            AssertRCReturn(rc, rc);
            if (ptl->img)
            {
                CRASSERT(ptl->bytes);

                ptl->img = crAlloc(ptl->bytes);
                if (!ptl->img) return VERR_NO_MEMORY;

                rc = SSMR3GetMem(pSSM, ptl->img, ptl->bytes);
                AssertRCReturn(rc, rc);
            }
#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
            /* Same story as in crStateSaveTextureObjData */
            else if (ptl->bytes)
            {
                ptl->img = crAlloc(ptl->bytes);
                if (!ptl->img) return VERR_NO_MEMORY;

                rc = SSMR3GetMem(pSSM, ptl->img, ptl->bytes);
                AssertRCReturn(rc, rc);
            }
#endif
            crStateTextureInitTextureFormat(ptl, ptl->internalFormat);
        }
    }

    return VINF_SUCCESS;
}

static void crStateSaveSharedTextureCB(unsigned long key, void *data1, void *data2)
{
    CRTextureObj *pTexture = (CRTextureObj *) data1;
    PSSMHANDLE pSSM = (PSSMHANDLE) data2;
    int32_t rc;

    CRASSERT(pTexture && pSSM);

    rc = SSMR3PutMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);
    rc = SSMR3PutMem(pSSM, pTexture, sizeof(*pTexture));
    CRASSERT(rc == VINF_SUCCESS);
    rc = crStateSaveTextureObjData(pTexture, pSSM);
    CRASSERT(rc == VINF_SUCCESS);
}

static int32_t crStateSaveMatrixStack(CRMatrixStack *pStack, PSSMHANDLE pSSM)
{
    return SSMR3PutMem(pSSM, pStack->stack, sizeof(CRmatrix) * pStack->maxDepth);
}

static int32_t crStateLoadMatrixStack(CRMatrixStack *pStack, PSSMHANDLE pSSM)
{
    int32_t rc;

    CRASSERT(pStack && pSSM);

    rc = SSMR3GetMem(pSSM, pStack->stack, sizeof(CRmatrix) * pStack->maxDepth);
    /* fixup stack top pointer */
    pStack->top = &pStack->stack[pStack->depth];
    return rc;
}

static int32_t crStateSaveTextureObjPtr(CRTextureObj *pTexture, PSSMHANDLE pSSM)
{
    /* Current texture pointer can't be NULL for real texture unit states,
     * but it could be NULL for unused attribute stack depths.
     */
    if (pTexture)
        return SSMR3PutU32(pSSM, pTexture->id);
    else
        return VINF_SUCCESS;
}

static int32_t crStateLoadTextureObjPtr(CRTextureObj **pTexture, CRContext *pContext, GLenum target, PSSMHANDLE pSSM)
{
    uint32_t texName;
    int32_t rc;

    /* We're loading attrib stack with unused state */
    if (!*pTexture)
        return VINF_SUCCESS;

    rc = SSMR3GetU32(pSSM, &texName);
    AssertRCReturn(rc, rc);

    if (texName)
    {
        *pTexture = (CRTextureObj *) crHashtableSearch(pContext->shared->textureTable, texName);
    }
    else
    {
        switch (target)
        {
            case GL_TEXTURE_1D:
                *pTexture = &(pContext->texture.base1D);
                break;
            case GL_TEXTURE_2D:
                *pTexture = &(pContext->texture.base2D);
                break;
#ifdef CR_OPENGL_VERSION_1_2
            case GL_TEXTURE_3D:
                *pTexture = &(pContext->texture.base3D);
                break;
#endif
#ifdef CR_ARB_texture_cube_map
            case GL_TEXTURE_CUBE_MAP_ARB:
                *pTexture = &(pContext->texture.baseCubeMap);
                break;
#endif
#ifdef CR_NV_texture_rectangle
            case GL_TEXTURE_RECTANGLE_NV:
                *pTexture = &(pContext->texture.baseRect);
                break;
#endif
            default:
                crError("LoadTextureObjPtr: Unknown texture target %d", target);
        }
    }

    return rc;
}

static int32_t crStateSaveTexUnitCurrentTexturePtrs(CRTextureUnit *pTexUnit, PSSMHANDLE pSSM)
{
    int32_t rc;

    rc = crStateSaveTextureObjPtr(pTexUnit->currentTexture1D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjPtr(pTexUnit->currentTexture2D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjPtr(pTexUnit->currentTexture3D, pSSM);
    AssertRCReturn(rc, rc);
#ifdef CR_ARB_texture_cube_map
    rc = crStateSaveTextureObjPtr(pTexUnit->currentTextureCubeMap, pSSM);
    AssertRCReturn(rc, rc);
#endif
#ifdef CR_NV_texture_rectangle
    rc = crStateSaveTextureObjPtr(pTexUnit->currentTextureRect, pSSM);
    AssertRCReturn(rc, rc);
#endif

    return rc;
}

static int32_t crStateLoadTexUnitCurrentTexturePtrs(CRTextureUnit *pTexUnit, CRContext *pContext, PSSMHANDLE pSSM)
{
    int32_t rc;

    rc = crStateLoadTextureObjPtr(&pTexUnit->currentTexture1D, pContext, GL_TEXTURE_1D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjPtr(&pTexUnit->currentTexture2D, pContext, GL_TEXTURE_1D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjPtr(&pTexUnit->currentTexture3D, pContext, GL_TEXTURE_2D, pSSM);
    AssertRCReturn(rc, rc);
#ifdef CR_ARB_texture_cube_map
    rc = crStateLoadTextureObjPtr(&pTexUnit->currentTextureCubeMap, pContext, GL_TEXTURE_CUBE_MAP_ARB, pSSM);
    AssertRCReturn(rc, rc);
#endif
#ifdef CR_NV_texture_rectangle
    rc = crStateLoadTextureObjPtr(&pTexUnit->currentTextureRect, pContext, GL_TEXTURE_RECTANGLE_NV, pSSM);
    AssertRCReturn(rc, rc);
#endif

    return rc;
}

static int32_t crSateSaveEvalCoeffs1D(CREvaluator1D *pEval, PSSMHANDLE pSSM)
{
    int32_t rc, i;

    for (i=0; i<GLEVAL_TOT; ++i)
    {
        if (pEval[i].coeff)
        {
            rc = SSMR3PutMem(pSSM, pEval[i].coeff, pEval[i].order * gleval_sizes[i] * sizeof(GLfloat));
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}

static int32_t crSateSaveEvalCoeffs2D(CREvaluator2D *pEval, PSSMHANDLE pSSM)
{
    int32_t rc, i;

    for (i=0; i<GLEVAL_TOT; ++i)
    {
        if (pEval[i].coeff)
        {
            rc = SSMR3PutMem(pSSM, pEval[i].coeff, pEval[i].uorder * pEval[i].vorder * gleval_sizes[i] * sizeof(GLfloat));
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}

static int32_t crSateLoadEvalCoeffs1D(CREvaluator1D *pEval, GLboolean bReallocMem, PSSMHANDLE pSSM)
{
    int32_t rc, i;
    size_t size;

    for (i=0; i<GLEVAL_TOT; ++i)
    {
        if (pEval[i].coeff)
        {
            size = pEval[i].order * gleval_sizes[i] * sizeof(GLfloat);
            if (bReallocMem)
            {
                pEval[i].coeff = (GLfloat*) crAlloc(size);
                if (!pEval[i].coeff) return VERR_NO_MEMORY;
            }
            rc = SSMR3GetMem(pSSM, pEval[i].coeff, size);
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}

static int32_t crSateLoadEvalCoeffs2D(CREvaluator2D *pEval, GLboolean bReallocMem, PSSMHANDLE pSSM)
{
    int32_t rc, i;
    size_t size;

    for (i=0; i<GLEVAL_TOT; ++i)
    {
        if (pEval[i].coeff)
        {
            size = pEval[i].uorder * pEval[i].vorder * gleval_sizes[i] * sizeof(GLfloat);
            if (bReallocMem)
            {
                pEval[i].coeff = (GLfloat*) crAlloc(size);
                if (!pEval[i].coeff) return VERR_NO_MEMORY;
            }
            rc = SSMR3GetMem(pSSM, pEval[i].coeff, size);
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}

static void crStateCopyEvalPtrs1D(CREvaluator1D *pDst, CREvaluator1D *pSrc)
{
    int32_t i;

    for (i=0; i<GLEVAL_TOT; ++i)
        pDst[i].coeff = pSrc[i].coeff;

    /*
    pDst[GL_MAP1_VERTEX_3-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_VERTEX_3-GL_MAP1_COLOR_4].coeff;
    pDst[GL_MAP1_VERTEX_4-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_VERTEX_4-GL_MAP1_COLOR_4].coeff;
    pDst[GL_MAP1_INDEX-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_INDEX-GL_MAP1_COLOR_4].coeff;
    pDst[GL_MAP1_COLOR_4-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_COLOR_4-GL_MAP1_COLOR_4].coeff;
    pDst[GL_MAP1_NORMAL-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_NORMAL-GL_MAP1_COLOR_4].coeff;
    pDst[GL_MAP1_TEXTURE_COORD_1-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_TEXTURE_COORD_1-GL_MAP1_COLOR_4].coeff;
    pDst[GL_MAP1_TEXTURE_COORD_2-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_TEXTURE_COORD_2-GL_MAP1_COLOR_4].coeff;
    pDst[GL_MAP1_TEXTURE_COORD_3-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_TEXTURE_COORD_3-GL_MAP1_COLOR_4].coeff;
    pDst[GL_MAP1_TEXTURE_COORD_4-GL_MAP1_COLOR_4].coeff = pSrc[GL_MAP1_TEXTURE_COORD_4-GL_MAP1_COLOR_4].coeff;
    */
}

static void crStateCopyEvalPtrs2D(CREvaluator2D *pDst, CREvaluator2D *pSrc)
{
    int32_t i;

    for (i=0; i<GLEVAL_TOT; ++i)
        pDst[i].coeff = pSrc[i].coeff;

    /*
    pDst[GL_MAP2_VERTEX_3-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_VERTEX_3-GL_MAP2_COLOR_4].coeff;
    pDst[GL_MAP2_VERTEX_4-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_VERTEX_4-GL_MAP2_COLOR_4].coeff;
    pDst[GL_MAP2_INDEX-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_INDEX-GL_MAP2_COLOR_4].coeff;
    pDst[GL_MAP2_COLOR_4-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_COLOR_4-GL_MAP2_COLOR_4].coeff;
    pDst[GL_MAP2_NORMAL-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_NORMAL-GL_MAP2_COLOR_4].coeff;
    pDst[GL_MAP2_TEXTURE_COORD_1-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_TEXTURE_COORD_1-GL_MAP2_COLOR_4].coeff;
    pDst[GL_MAP2_TEXTURE_COORD_2-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_TEXTURE_COORD_2-GL_MAP2_COLOR_4].coeff;
    pDst[GL_MAP2_TEXTURE_COORD_3-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_TEXTURE_COORD_3-GL_MAP2_COLOR_4].coeff;
    pDst[GL_MAP2_TEXTURE_COORD_4-GL_MAP2_COLOR_4].coeff = pSrc[GL_MAP2_TEXTURE_COORD_4-GL_MAP2_COLOR_4].coeff;
    */
}

static void crStateSaveBufferObjectCB(unsigned long key, void *data1, void *data2)
{
    CRBufferObject *pBufferObj = (CRBufferObject *) data1;
    PSSMHANDLE pSSM = (PSSMHANDLE) data2;
    int32_t rc;

    CRASSERT(pBufferObj && pSSM);

    rc = SSMR3PutMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);
    rc = SSMR3PutMem(pSSM, pBufferObj, sizeof(*pBufferObj));
    CRASSERT(rc == VINF_SUCCESS);

    if (pBufferObj->data)
    {
        /*We could get here even though retainBufferData is false on host side, in case when we're taking snapshot
          after state load and before this context was ever made current*/
        CRASSERT(pBufferObj->size>0);
        rc = SSMR3PutMem(pSSM, pBufferObj->data, pBufferObj->size);
        CRASSERT(rc == VINF_SUCCESS);
    }
    else if (pBufferObj->id!=0 && pBufferObj->size>0)
    {
        diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, pBufferObj->hwid);
        pBufferObj->pointer = diff_api.MapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_ONLY_ARB);
        rc = SSMR3PutMem(pSSM, &pBufferObj->pointer, sizeof(pBufferObj->pointer));
        CRASSERT(rc == VINF_SUCCESS);
        if (pBufferObj->pointer)
        {
            rc = SSMR3PutMem(pSSM, pBufferObj->pointer, pBufferObj->size);
            CRASSERT(rc == VINF_SUCCESS);
        }
        diff_api.UnmapBufferARB(GL_ARRAY_BUFFER_ARB);
        pBufferObj->pointer = NULL;
    }
}

static void crStateSaveProgramCB(unsigned long key, void *data1, void *data2)
{
    CRProgram *pProgram = (CRProgram *) data1;
    PSSMHANDLE pSSM = (PSSMHANDLE) data2;
    CRProgramSymbol *pSymbol;
    int32_t rc;

    CRASSERT(pProgram && pSSM);

    rc = SSMR3PutMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);
    rc = SSMR3PutMem(pSSM, pProgram, sizeof(*pProgram));
    CRASSERT(rc == VINF_SUCCESS);
    if (pProgram->string)
    {
        CRASSERT(pProgram->length);
        rc = SSMR3PutMem(pSSM, pProgram->string, pProgram->length);
        CRASSERT(rc == VINF_SUCCESS);
    }

    for (pSymbol = pProgram->symbolTable; pSymbol; pSymbol=pSymbol->next)
    {
        rc = SSMR3PutMem(pSSM, pSymbol, sizeof(*pSymbol));
        CRASSERT(rc == VINF_SUCCESS);
        if (pSymbol->name)
        {
            CRASSERT(pSymbol->cbName>0);
            rc = SSMR3PutMem(pSSM, pSymbol->name, pSymbol->cbName);
            CRASSERT(rc == VINF_SUCCESS);
        }
    }
}

static void crStateSaveFramebuffersCB(unsigned long key, void *data1, void *data2)
{
    CRFramebufferObject *pFBO = (CRFramebufferObject*) data1;
    PSSMHANDLE pSSM = (PSSMHANDLE) data2;
    int32_t rc;

    rc = SSMR3PutMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);

    rc = SSMR3PutMem(pSSM, pFBO, sizeof(*pFBO));
    CRASSERT(rc == VINF_SUCCESS);
}

static void crStateSaveRenderbuffersCB(unsigned long key, void *data1, void *data2)
{
    CRRenderbufferObject *pRBO = (CRRenderbufferObject*) data1;
    PSSMHANDLE pSSM = (PSSMHANDLE) data2;
    int32_t rc;

    rc = SSMR3PutMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);

    rc = SSMR3PutMem(pSSM, pRBO, sizeof(*pRBO));
    CRASSERT(rc == VINF_SUCCESS);
}

static int32_t crStateLoadProgram(CRProgram **ppProgram, PSSMHANDLE pSSM)
{
    CRProgramSymbol **ppSymbol;
    int32_t rc;
    unsigned long key;

    rc = SSMR3GetMem(pSSM, &key, sizeof(key));
    AssertRCReturn(rc, rc);

    /* we're loading default vertex or pixel program*/
    if (*ppProgram)
    {
        if (key!=0) return VERR_SSM_UNEXPECTED_DATA;
    }
    else
    {
        *ppProgram = (CRProgram*) crAlloc(sizeof(CRProgram));
        if (!ppProgram) return VERR_NO_MEMORY;
        if (key==0) return VERR_SSM_UNEXPECTED_DATA;
    }

    rc = SSMR3GetMem(pSSM, *ppProgram, sizeof(**ppProgram));
    AssertRCReturn(rc, rc);

    if ((*ppProgram)->string)
    {
        CRASSERT((*ppProgram)->length);
        (*ppProgram)->string = crAlloc((*ppProgram)->length);
        if (!(*ppProgram)->string) return VERR_NO_MEMORY;
        rc = SSMR3GetMem(pSSM, (void*) (*ppProgram)->string, (*ppProgram)->length);
        AssertRCReturn(rc, rc);
    }

    for (ppSymbol = &(*ppProgram)->symbolTable; *ppSymbol; ppSymbol=&(*ppSymbol)->next)
    {
        *ppSymbol = crAlloc(sizeof(CRProgramSymbol));
        if (!ppSymbol) return VERR_NO_MEMORY;

        rc = SSMR3GetMem(pSSM, *ppSymbol, sizeof(**ppSymbol));
        AssertRCReturn(rc, rc);

        if ((*ppSymbol)->name)
        {
            CRASSERT((*ppSymbol)->cbName>0);
            (*ppSymbol)->name = crAlloc((*ppSymbol)->cbName);
            if (!(*ppSymbol)->name) return VERR_NO_MEMORY;

            rc = SSMR3GetMem(pSSM, (void*) (*ppSymbol)->name, (*ppSymbol)->cbName);
            AssertRCReturn(rc, rc);
        }
    }

    return VINF_SUCCESS;
}

static void crStateSaveString(const char *pStr, PSSMHANDLE pSSM)
{
    int32_t len;
    int32_t rc;

    if (pStr)
    {
        len = crStrlen(pStr)+1;

        rc = SSMR3PutS32(pSSM, len);
        CRASSERT(rc == VINF_SUCCESS);

        rc = SSMR3PutMem(pSSM, pStr, len*sizeof(*pStr));
        CRASSERT(rc == VINF_SUCCESS);
    }
    else
    {
        rc = SSMR3PutS32(pSSM, 0);
        CRASSERT(rc == VINF_SUCCESS);
    }
}

static char* crStateLoadString(PSSMHANDLE pSSM)
{
    int32_t len, rc;
    char* pStr = NULL;

    rc = SSMR3GetS32(pSSM, &len);
    CRASSERT(rc == VINF_SUCCESS);

    if (len!=0)
    {
        pStr = crAlloc(len*sizeof(*pStr));

        rc = SSMR3GetMem(pSSM, pStr, len*sizeof(*pStr));
        CRASSERT(rc == VINF_SUCCESS);
    }

    return pStr;
}

static void crStateSaveGLSLShaderCB(unsigned long key, void *data1, void *data2)
{
    CRGLSLShader *pShader = (CRGLSLShader*) data1;
    PSSMHANDLE pSSM = (PSSMHANDLE) data2;
    int32_t rc;

    rc = SSMR3PutMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);

    rc = SSMR3PutMem(pSSM, pShader, sizeof(*pShader));
    CRASSERT(rc == VINF_SUCCESS);

    if (pShader->source)
    {
        crStateSaveString(pShader->source, pSSM);
    }
    else
    {
        GLint sLen=0;
        GLchar *source=NULL;

        diff_api.GetShaderiv(pShader->hwid, GL_SHADER_SOURCE_LENGTH, &sLen);
        if (sLen>0)
        {
            source = (GLchar*) crAlloc(sLen);
            diff_api.GetShaderSource(pShader->hwid, sLen, NULL, source);
        }

        crStateSaveString(source, pSSM);
        if (source) crFree(source);
    }
}

static CRGLSLShader* crStateLoadGLSLShader(PSSMHANDLE pSSM)
{
    CRGLSLShader *pShader;
    int32_t rc;
    unsigned long key;

    pShader = crAlloc(sizeof(*pShader));
    if (!pShader) return NULL;

    rc = SSMR3GetMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);

    rc = SSMR3GetMem(pSSM, pShader, sizeof(*pShader));
    CRASSERT(rc == VINF_SUCCESS);

    pShader->source = crStateLoadString(pSSM);

    return pShader;
}

static void crStateSaveGLSLShaderKeyCB(unsigned long key, void *data1, void *data2)
{
    CRGLSLShader *pShader = (CRGLSLShader*) data1;
    PSSMHANDLE pSSM = (PSSMHANDLE) data2;
    int32_t rc;

    rc = SSMR3PutMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);
}

static void crStateSaveGLSLProgramAttribs(CRGLSLProgramState *pState, PSSMHANDLE pSSM)
{
    GLuint i;
    int32_t rc;

    for (i=0; i<pState->cAttribs; ++i)
    {
        rc = SSMR3PutMem(pSSM, &pState->pAttribs[i].index, sizeof(pState->pAttribs[i].index));
        CRASSERT(rc == VINF_SUCCESS);
        crStateSaveString(pState->pAttribs[i].name, pSSM);
    }
}

static void crStateSaveGLSLProgramCB(unsigned long key, void *data1, void *data2)
{
    CRGLSLProgram *pProgram = (CRGLSLProgram*) data1;
    PSSMHANDLE pSSM = (PSSMHANDLE) data2;
    int32_t rc;
    uint32_t ui32;
    GLint maxUniformLen, activeUniforms=0, uniformsCount=0, i, j;
    GLchar *name = NULL;
    GLenum type;
    GLint size, location;

    rc = SSMR3PutMem(pSSM, &key, sizeof(key));
    CRASSERT(rc == VINF_SUCCESS);

    rc = SSMR3PutMem(pSSM, pProgram, sizeof(*pProgram));
    CRASSERT(rc == VINF_SUCCESS);

    ui32 = crHashtableNumElements(pProgram->currentState.attachedShaders);
    rc = SSMR3PutU32(pSSM, ui32);
    CRASSERT(rc == VINF_SUCCESS);

    crHashtableWalk(pProgram->currentState.attachedShaders, crStateSaveGLSLShaderKeyCB, pSSM);

    if (pProgram->activeState.attachedShaders)
    {
        ui32 = crHashtableNumElements(pProgram->activeState.attachedShaders);
        rc = SSMR3PutU32(pSSM, ui32);
        CRASSERT(rc == VINF_SUCCESS);
        crHashtableWalk(pProgram->currentState.attachedShaders, crStateSaveGLSLShaderCB, pSSM);
    }

    crStateSaveGLSLProgramAttribs(&pProgram->currentState, pSSM);
    crStateSaveGLSLProgramAttribs(&pProgram->activeState, pSSM);

    diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);
    diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_UNIFORMS, &activeUniforms);

    if (!maxUniformLen)
    {
        if (activeUniforms)
        {
            crWarning("activeUniforms (%d), while maxUniformLen is zero", activeUniforms);
            activeUniforms = 0;
        }
    }

    if (activeUniforms>0)
    {
        name = (GLchar *) crAlloc((maxUniformLen+8)*sizeof(GLchar));

        if (!name)
        {
            crWarning("crStateSaveGLSLProgramCB: out of memory");
            return;
        }
    }

    for (i=0; i<activeUniforms; ++i)
    {
        diff_api.GetActiveUniform(pProgram->hwid, i, maxUniformLen, NULL, &size, &type, name);
        uniformsCount += size;
    }
    CRASSERT(uniformsCount>=activeUniforms);

    rc = SSMR3PutS32(pSSM, uniformsCount);
    CRASSERT(rc == VINF_SUCCESS);

    if (activeUniforms>0)
    {
        GLfloat fdata[16];
        GLint idata[16];
        char *pIndexStr=NULL;

        for (i=0; i<activeUniforms; ++i)
        {
            diff_api.GetActiveUniform(pProgram->hwid, i, maxUniformLen, NULL, &size, &type, name);

            if (size>1)
            {
                pIndexStr = crStrchr(name, '[');
                if (!pIndexStr)
                {
                    pIndexStr = name+crStrlen(name);
                }
            }

            for (j=0; j<size; ++j)
            {
                if (size>1)
                {
                    sprintf(pIndexStr, "[%i]", j);
                }
                location = diff_api.GetUniformLocation(pProgram->hwid, name);

                rc = SSMR3PutMem(pSSM, &type, sizeof(type));
                CRASSERT(rc == VINF_SUCCESS);

                crStateSaveString(name, pSSM);

                if (crStateIsIntUniform(type))
                {
                    diff_api.GetUniformiv(pProgram->hwid, location, &idata[0]);
                    rc = SSMR3PutMem(pSSM, &idata[0], crStateGetUniformSize(type)*sizeof(idata[0]));
                    CRASSERT(rc == VINF_SUCCESS);
                }
                else
                {
                    diff_api.GetUniformfv(pProgram->hwid, location, &fdata[0]);
                    rc = SSMR3PutMem(pSSM, &fdata[0], crStateGetUniformSize(type)*sizeof(fdata[0]));
                    CRASSERT(rc == VINF_SUCCESS);
                }
            }
        }

        crFree(name);
    }
}

static int32_t crStateSaveClientPointer(CRVertexArrays *pArrays, int32_t index, PSSMHANDLE pSSM)
{
    int32_t rc;
    CRClientPointer *cp;

    cp = crStateGetClientPointerByIndex(index, pArrays);

    if (cp->buffer)
        rc = SSMR3PutU32(pSSM, cp->buffer->id);
    else
        rc = SSMR3PutU32(pSSM, 0);

    AssertRCReturn(rc, rc);

#ifdef CR_EXT_compiled_vertex_array
    if (cp->locked)
    {
        CRASSERT(cp->p);
        rc = SSMR3PutMem(pSSM, cp->p, cp->stride*(pArrays->lockFirst+pArrays->lockCount));
        AssertRCReturn(rc, rc);
    }
#endif

    return VINF_SUCCESS;
}

static int32_t crStateLoadClientPointer(CRVertexArrays *pArrays, int32_t index, CRContext *pContext, PSSMHANDLE pSSM)
{
    int32_t rc;
    uint32_t ui;
    CRClientPointer *cp;

    cp = crStateGetClientPointerByIndex(index, pArrays);

    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    cp->buffer = ui==0 ? pContext->bufferobject.nullBuffer : crHashtableSearch(pContext->shared->buffersTable, ui);

    if (!cp->buffer)
    {
        crWarning("crStateLoadClientPointer: ui=%d loaded as NULL buffer!", ui);
    }

#ifdef CR_EXT_compiled_vertex_array
    if (cp->locked)
    {
        rc = crStateAllocAndSSMR3GetMem(pSSM, (void**)&cp->p, cp->stride*(pArrays->lockFirst+pArrays->lockCount));
        AssertRCReturn(rc, rc);
    }
#endif

    return VINF_SUCCESS;
}

static int32_t crStateSaveCurrentBits(CRStateBits *pBits, PSSMHANDLE pSSM)
{
    int32_t rc, i;

    rc = SSMR3PutMem(pSSM, pBits, sizeof(*pBits));
    AssertRCReturn(rc, rc);

    rc = SSMR3PutMem(pSSM, pBits->client.v, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    rc = SSMR3PutMem(pSSM, pBits->client.n, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    rc = SSMR3PutMem(pSSM, pBits->client.c, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    rc = SSMR3PutMem(pSSM, pBits->client.s, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    rc = SSMR3PutMem(pSSM, pBits->client.i, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    for (i=0; i<CR_MAX_TEXTURE_UNITS; i++)
    {
        rc = SSMR3PutMem(pSSM, pBits->client.t[i], GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
        AssertRCReturn(rc, rc);
    }
    rc = SSMR3PutMem(pSSM, pBits->client.e, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    rc = SSMR3PutMem(pSSM, pBits->client.f, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
#ifdef CR_NV_vertex_program
    for (i=0; i<CR_MAX_VERTEX_ATTRIBS; i++)
    {
        rc = SSMR3PutMem(pSSM, pBits->client.a[i], GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
        AssertRCReturn(rc, rc);
    }
#endif

    rc = SSMR3PutMem(pSSM, pBits->lighting.light, CR_MAX_LIGHTS*sizeof(pBits->lighting.light));
    AssertRCReturn(rc, rc);

    return VINF_SUCCESS;
}

static int32_t crStateLoadCurrentBits(CRStateBits *pBits, PSSMHANDLE pSSM)
{
    int32_t rc, i;
    CRClientBits client;
    CRLightingBits lighting;

    CRASSERT(pBits);

    client.v = pBits->client.v;
    client.n = pBits->client.n;
    client.c = pBits->client.c;
    client.s = pBits->client.s;
    client.i = pBits->client.i;
    client.e = pBits->client.e;
    client.f = pBits->client.f;
    for (i=0; i<CR_MAX_TEXTURE_UNITS; i++)
    {
        client.t[i] = pBits->client.t[i];
    }
#ifdef CR_NV_vertex_program
    for (i=0; i<CR_MAX_VERTEX_ATTRIBS; i++)
    {
        client.a[i] = pBits->client.a[i];
    }
#endif
    lighting.light = pBits->lighting.light;

    rc = SSMR3GetMem(pSSM, pBits, sizeof(*pBits));
    AssertRCReturn(rc, rc);

    pBits->client.v = client.v;
    rc = SSMR3GetMem(pSSM, pBits->client.v, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    pBits->client.n = client.n;
    rc = SSMR3GetMem(pSSM, pBits->client.n, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    pBits->client.c = client.c;
    rc = SSMR3GetMem(pSSM, pBits->client.c, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    pBits->client.s = client.s;
    rc = SSMR3GetMem(pSSM, pBits->client.s, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    pBits->client.i = client.i;
    rc = SSMR3GetMem(pSSM, pBits->client.i, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    pBits->client.e = client.e;
    rc = SSMR3GetMem(pSSM, pBits->client.e, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    pBits->client.f = client.f;
    rc = SSMR3GetMem(pSSM, pBits->client.f, GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
    AssertRCReturn(rc, rc);
    for (i=0; i<CR_MAX_TEXTURE_UNITS; i++)
    {
        pBits->client.t[i] = client.t[i];
        rc = SSMR3GetMem(pSSM, pBits->client.t[i], GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
        AssertRCReturn(rc, rc);
    }
#ifdef CR_NV_vertex_program
    for (i=0; i<CR_MAX_VERTEX_ATTRIBS; i++)
    {
        pBits->client.a[i] = client.a[i];
        rc = SSMR3GetMem(pSSM, pBits->client.a[i], GLCLIENT_BIT_ALLOC*sizeof(CRbitvalue));
        AssertRCReturn(rc, rc);
    }
#endif

    pBits->lighting.light = lighting.light;
    rc = SSMR3GetMem(pSSM, pBits->lighting.light, CR_MAX_LIGHTS*sizeof(pBits->lighting.light));
    AssertRCReturn(rc, rc);

    return VINF_SUCCESS;
}

static void crStateSaveKeysCB(unsigned long firstKey, unsigned long count, void *data)
{
    PSSMHANDLE pSSM = (PSSMHANDLE)data;
    int rc;
    CRASSERT(firstKey);
    CRASSERT(count);
    rc = SSMR3PutU32(pSSM, firstKey);
    CRASSERT(RT_SUCCESS(rc));
    rc = SSMR3PutU32(pSSM, count);
    CRASSERT(RT_SUCCESS(rc));
}

static int32_t crStateSaveKeys(CRHashTable *pHash, PSSMHANDLE pSSM)
{
    crHashtableWalkKeys(pHash, crStateSaveKeysCB , pSSM);
    /* use null terminator */
    SSMR3PutU32(pSSM, 0);
    return VINF_SUCCESS;
}

static int32_t crStateLoadKeys(CRHashTable *pHash, PSSMHANDLE pSSM, uint32_t u32Version)
{
    uint32_t u32Key, u32Count, i;
    int rc;
    for(;;)
    {
        rc = SSMR3GetU32(pSSM, &u32Key);
        AssertRCReturn(rc, rc);

        if (!u32Key)
            return rc;

        rc = SSMR3GetU32(pSSM, &u32Count);
        AssertRCReturn(rc, rc);

        CRASSERT(u32Count);

        if (u32Version > SHCROGL_SSM_VERSION_WITH_BUGGY_KEYS)
        {
            for (i = u32Key; i < u32Count + u32Key; ++i)
            {
                GLboolean fIsNew = crHashtableAllocRegisterKey(pHash, i);
#if 0 //def DEBUG_misha
                CRASSERT(fIsNew);
#endif
            }
        }
    }

    return rc;
}

int32_t crStateSaveContext(CRContext *pContext, PSSMHANDLE pSSM)
{
    int32_t rc, i;
    uint32_t ui32, j;
    GLboolean bSaveShared = GL_TRUE;

    CRASSERT(pContext && pSSM);

    CRASSERT(pContext->client.attribStackDepth == 0);

    /* this stuff is not used anymore, zero it up for sanity */
    pContext->buffer.storedWidth = 0;
    pContext->buffer.storedHeight = 0;

    CRASSERT(VBoxTlsRefIsFunctional(pContext));

    /* make sure the gl error state is captured by our state mechanism to store the correct gl  error value */
    crStateSyncHWErrorState(pContext);

    rc = SSMR3PutMem(pSSM, pContext, sizeof (*pContext));
    AssertRCReturn(rc, rc);

    if (crHashtableNumElements(pContext->shared->dlistTable)>0)
        crWarning("Saving state with %d display lists, unsupported", crHashtableNumElements(pContext->shared->dlistTable));

    if (crHashtableNumElements(pContext->program.programHash)>0)
        crDebug("Saving state with %d programs", crHashtableNumElements(pContext->program.programHash));

    rc = SSMR3PutS32(pSSM, pContext->shared->id);
    AssertRCReturn(rc, rc);

    rc = SSMR3PutS32(pSSM, crStateContextIsShared(pContext));
    AssertRCReturn(rc, rc);

    if (pContext->shared->refCount>1)
    {
        bSaveShared = pContext->shared->saveCount==0;

        ++pContext->shared->saveCount;
        if (pContext->shared->saveCount == pContext->shared->refCount)
        {
            pContext->shared->saveCount=0;
        }
    }

    /* Save transform state */
    rc = SSMR3PutMem(pSSM, pContext->transform.clipPlane, sizeof(GLvectord)*CR_MAX_CLIP_PLANES);
    AssertRCReturn(rc, rc);
    rc = SSMR3PutMem(pSSM, pContext->transform.clip, sizeof(GLboolean)*CR_MAX_CLIP_PLANES);
    AssertRCReturn(rc, rc);
    rc = crStateSaveMatrixStack(&pContext->transform.modelViewStack, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveMatrixStack(&pContext->transform.projectionStack, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveMatrixStack(&pContext->transform.colorStack, pSSM);
    AssertRCReturn(rc, rc);
    for (i = 0 ; i < CR_MAX_TEXTURE_UNITS ; i++)
    {
        rc = crStateSaveMatrixStack(&pContext->transform.textureStack[i], pSSM);
        AssertRCReturn(rc, rc);
    }
    for (i = 0 ; i < CR_MAX_PROGRAM_MATRICES ; i++)
    {
        rc = crStateSaveMatrixStack(&pContext->transform.programStack[i], pSSM);
        AssertRCReturn(rc, rc);
    }

    /* Save textures */
    rc = crStateSaveTextureObjData(&pContext->texture.base1D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjData(&pContext->texture.base2D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjData(&pContext->texture.base3D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjData(&pContext->texture.proxy1D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjData(&pContext->texture.proxy2D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjData(&pContext->texture.proxy3D, pSSM);
#ifdef CR_ARB_texture_cube_map
    rc = crStateSaveTextureObjData(&pContext->texture.baseCubeMap, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjData(&pContext->texture.proxyCubeMap, pSSM);
    AssertRCReturn(rc, rc);
#endif
#ifdef CR_NV_texture_rectangle
    rc = crStateSaveTextureObjData(&pContext->texture.baseRect, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateSaveTextureObjData(&pContext->texture.proxyRect, pSSM);
    AssertRCReturn(rc, rc);
#endif

    /* Save shared textures */
    if (bSaveShared)
    {
        CRASSERT(pContext->shared && pContext->shared->textureTable);
        rc = crStateSaveKeys(pContext->shared->textureTable, pSSM);
        AssertRCReturn(rc, rc);
        ui32 = crHashtableNumElements(pContext->shared->textureTable);
        rc = SSMR3PutU32(pSSM, ui32);
        AssertRCReturn(rc, rc);
        crHashtableWalk(pContext->shared->textureTable, crStateSaveSharedTextureCB, pSSM);

#ifdef CR_STATE_NO_TEXTURE_IMAGE_STORE
        /* Restore previous texture bindings via diff_api */
        if (ui32)
        {
            CRTextureUnit *pTexUnit;

            pTexUnit = &pContext->texture.unit[pContext->texture.curTextureUnit];

            diff_api.BindTexture(GL_TEXTURE_1D, pTexUnit->currentTexture1D->hwid);
            diff_api.BindTexture(GL_TEXTURE_2D, pTexUnit->currentTexture2D->hwid);
            diff_api.BindTexture(GL_TEXTURE_3D, pTexUnit->currentTexture3D->hwid);
#ifdef CR_ARB_texture_cube_map
            diff_api.BindTexture(GL_TEXTURE_CUBE_MAP_ARB, pTexUnit->currentTextureCubeMap->hwid);
#endif
#ifdef CR_NV_texture_rectangle
            diff_api.BindTexture(GL_TEXTURE_RECTANGLE_NV, pTexUnit->currentTextureRect->hwid);
#endif
        }
#endif
    }

    /* Save current texture pointers */
    for (i=0; i<CR_MAX_TEXTURE_UNITS; ++i)
    {
        rc = crStateSaveTexUnitCurrentTexturePtrs(&pContext->texture.unit[i], pSSM);
        AssertRCReturn(rc, rc);
    }

    /* Save lights */
    CRASSERT(pContext->lighting.light);
    rc = SSMR3PutMem(pSSM, pContext->lighting.light, CR_MAX_LIGHTS * sizeof(*pContext->lighting.light));
    AssertRCReturn(rc, rc);

    /* Save attrib stack*/
    /*@todo could go up to used stack depth here?*/
    for ( i = 0 ; i < CR_MAX_ATTRIB_STACK_DEPTH ; i++)
    {
        if (pContext->attrib.enableStack[i].clip)
        {
            rc = SSMR3PutMem(pSSM, pContext->attrib.enableStack[i].clip,
                             pContext->limits.maxClipPlanes*sizeof(GLboolean));
            AssertRCReturn(rc, rc);
        }

        if (pContext->attrib.enableStack[i].light)
        {
            rc = SSMR3PutMem(pSSM, pContext->attrib.enableStack[i].light,
                             pContext->limits.maxLights*sizeof(GLboolean));
            AssertRCReturn(rc, rc);
        }

        if (pContext->attrib.lightingStack[i].light)
        {
            rc = SSMR3PutMem(pSSM, pContext->attrib.lightingStack[i].light,
                             pContext->limits.maxLights*sizeof(CRLight));
            AssertRCReturn(rc, rc);
        }

        for (j=0; j<pContext->limits.maxTextureUnits; ++j)
        {
            rc = crStateSaveTexUnitCurrentTexturePtrs(&pContext->attrib.textureStack[i].unit[j], pSSM);
            AssertRCReturn(rc, rc);
        }

        if (pContext->attrib.transformStack[i].clip)
        {
            rc = SSMR3PutMem(pSSM, pContext->attrib.transformStack[i].clip,
                             pContext->limits.maxClipPlanes*sizeof(GLboolean));
            AssertRCReturn(rc, rc);
        }

        if (pContext->attrib.transformStack[i].clipPlane)
        {
            rc = SSMR3PutMem(pSSM, pContext->attrib.transformStack[i].clipPlane,
                             pContext->limits.maxClipPlanes*sizeof(GLvectord));
            AssertRCReturn(rc, rc);
        }

        rc = crSateSaveEvalCoeffs1D(pContext->attrib.evalStack[i].eval1D, pSSM);
        AssertRCReturn(rc, rc);
        rc = crSateSaveEvalCoeffs2D(pContext->attrib.evalStack[i].eval2D, pSSM);
        AssertRCReturn(rc, rc);
    }

    /* Save evaluator coeffs */
    rc = crSateSaveEvalCoeffs1D(pContext->eval.eval1D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crSateSaveEvalCoeffs2D(pContext->eval.eval2D, pSSM);
    AssertRCReturn(rc, rc);

#ifdef CR_ARB_vertex_buffer_object
    /* Save buffer objects */
    if (bSaveShared)
    {
        rc = crStateSaveKeys(pContext->shared->buffersTable, pSSM);
        AssertRCReturn(rc, rc);
    }
    ui32 = bSaveShared? crHashtableNumElements(pContext->shared->buffersTable):0;
    rc = SSMR3PutU32(pSSM, ui32);
    AssertRCReturn(rc, rc);

    /* Save default one*/
    crStateSaveBufferObjectCB(0, pContext->bufferobject.nullBuffer, pSSM);

    if (bSaveShared)
    {
        /* Save all the rest */
        crHashtableWalk(pContext->shared->buffersTable, crStateSaveBufferObjectCB, pSSM);
    }

    /* Restore binding */
    diff_api.BindBufferARB(GL_ARRAY_BUFFER_ARB, pContext->bufferobject.arrayBuffer->hwid);

    /* Save pointers */
    rc = SSMR3PutU32(pSSM, pContext->bufferobject.arrayBuffer->id);
    AssertRCReturn(rc, rc);
    rc = SSMR3PutU32(pSSM, pContext->bufferobject.elementsBuffer->id);
    AssertRCReturn(rc, rc);
#ifdef CR_ARB_pixel_buffer_object
    rc = SSMR3PutU32(pSSM, pContext->bufferobject.packBuffer->id);
    AssertRCReturn(rc, rc);
    rc = SSMR3PutU32(pSSM, pContext->bufferobject.unpackBuffer->id);
    AssertRCReturn(rc, rc);
#endif
    /* Save clint pointers and buffer bindings*/
    for (i=0; i<CRSTATECLIENT_MAX_VERTEXARRAYS; ++i)
    {
        rc = crStateSaveClientPointer(&pContext->client.array, i, pSSM);
        AssertRCReturn(rc, rc);
    }

    crDebug("client.vertexArrayStackDepth %i", pContext->client.vertexArrayStackDepth);
    for (i=0; i<pContext->client.vertexArrayStackDepth; ++i)
    {
        CRVertexArrays *pArray = &pContext->client.vertexArrayStack[i];
        for (j=0; j<CRSTATECLIENT_MAX_VERTEXARRAYS; ++j)
        {
            rc = crStateSaveClientPointer(pArray, j, pSSM);
            AssertRCReturn(rc, rc);
        }
    }
#endif /*CR_ARB_vertex_buffer_object*/

    /* Save pixel/vertex programs */
    ui32 = crHashtableNumElements(pContext->program.programHash);
    rc = SSMR3PutU32(pSSM, ui32);
    AssertRCReturn(rc, rc);
    /* Save defaults programs */
    crStateSaveProgramCB(0, pContext->program.defaultVertexProgram, pSSM);
    crStateSaveProgramCB(0, pContext->program.defaultFragmentProgram, pSSM);
    /* Save all the rest */
    crHashtableWalk(pContext->program.programHash, crStateSaveProgramCB, pSSM);
    /* Save Pointers */
    rc = SSMR3PutU32(pSSM, pContext->program.currentVertexProgram->id);
    AssertRCReturn(rc, rc);
    rc = SSMR3PutU32(pSSM, pContext->program.currentFragmentProgram->id);
    AssertRCReturn(rc, rc);
    /* This one is unused it seems*/
    CRASSERT(!pContext->program.errorString);

#ifdef CR_EXT_framebuffer_object
    /* Save FBOs */
    if (bSaveShared)
    {
        rc = crStateSaveKeys(pContext->shared->fbTable, pSSM);
        AssertRCReturn(rc, rc);
        ui32 = crHashtableNumElements(pContext->shared->fbTable);
        rc = SSMR3PutU32(pSSM, ui32);
        AssertRCReturn(rc, rc);
        crHashtableWalk(pContext->shared->fbTable, crStateSaveFramebuffersCB, pSSM);

        rc = crStateSaveKeys(pContext->shared->rbTable, pSSM);
        AssertRCReturn(rc, rc);
        ui32 = crHashtableNumElements(pContext->shared->rbTable);
        rc = SSMR3PutU32(pSSM, ui32);
        AssertRCReturn(rc, rc);
        crHashtableWalk(pContext->shared->rbTable, crStateSaveRenderbuffersCB, pSSM);
    }
    rc = SSMR3PutU32(pSSM, pContext->framebufferobject.drawFB?pContext->framebufferobject.drawFB->id:0);
    AssertRCReturn(rc, rc);
    rc = SSMR3PutU32(pSSM, pContext->framebufferobject.readFB?pContext->framebufferobject.readFB->id:0);
    AssertRCReturn(rc, rc);
    rc = SSMR3PutU32(pSSM, pContext->framebufferobject.renderbuffer?pContext->framebufferobject.renderbuffer->id:0);
    AssertRCReturn(rc, rc);
#endif

#ifdef CR_OPENGL_VERSION_2_0
    /* Save GLSL related info */
    ui32 = crHashtableNumElements(pContext->glsl.shaders);
    rc = SSMR3PutU32(pSSM, ui32);
    AssertRCReturn(rc, rc);
    crHashtableWalk(pContext->glsl.shaders, crStateSaveGLSLShaderCB, pSSM);
    ui32 = crHashtableNumElements(pContext->glsl.programs);
    rc = SSMR3PutU32(pSSM, ui32);
    AssertRCReturn(rc, rc);
    crHashtableWalk(pContext->glsl.programs, crStateSaveGLSLProgramCB, pSSM);
    rc = SSMR3PutU32(pSSM, pContext->glsl.activeProgram?pContext->glsl.activeProgram->id:0);
    AssertRCReturn(rc, rc);
#endif

    return VINF_SUCCESS;
}

typedef struct _crFindSharedCtxParms {
    PFNCRSTATE_CONTEXT_GET pfnCtxGet;
    CRContext *pSrcCtx, *pDstCtx;
} crFindSharedCtxParms_t;

static void crStateFindSharedCB(unsigned long key, void *data1, void *data2)
{
    crFindSharedCtxParms_t *pParms = (crFindSharedCtxParms_t *) data2;
    CRContext *pContext = pParms->pfnCtxGet(data1);
    (void) key;

    if (pContext!=pParms->pSrcCtx && pContext->shared->id==pParms->pSrcCtx->shared->id)
    {
        pParms->pDstCtx->shared = pContext->shared;
    }
}

int32_t crStateSaveGlobals(PSSMHANDLE pSSM)
{
    /* don't need that for now */
#if 0
    CRStateBits *pBits;
    int rc;

    CRASSERT(g_cContexts >= 1);
    if (g_cContexts <= 1)
        return VINF_SUCCESS;

    pBits = GetCurrentBits();
#define CRSTATE_BITS_OP(_var, _size) \
        rc = SSMR3PutMem(pSSM, (pBits->_var), _size); \
        AssertRCReturn(rc, rc);
#include "state_bits_globalop.h"
#undef CRSTATE_BITS_OP
#endif
    return VINF_SUCCESS;
}

int32_t crStateLoadGlobals(PSSMHANDLE pSSM, uint32_t u32Version)
{
    CRStateBits *pBits;
    int rc;
    CRASSERT(g_cContexts >= 1);
    if (g_cContexts <= 1)
        return VINF_SUCCESS;

    pBits = GetCurrentBits();

    if (u32Version >= SHCROGL_SSM_VERSION_WITH_STATE_BITS)
    {
#define CRSTATE_BITS_OP(_var, _size) \
            rc = SSMR3GetMem(pSSM, (pBits->_var), _size); \
            AssertRCReturn(rc, rc);

        if (u32Version < SHCROGL_SSM_VERSION_WITH_FIXED_STENCIL)
        {
#define CRSTATE_BITS_OP_VERSION (SHCROGL_SSM_VERSION_WITH_FIXED_STENCIL - 1)
#define CRSTATE_BITS_OP_STENCIL_FUNC_V_33(_i, _var) do {} while (0)
#define CRSTATE_BITS_OP_STENCIL_OP_V_33(_i, _var) do {} while (0)
#include "state_bits_globalop.h"
#undef CRSTATE_BITS_OP_VERSION
#undef CRSTATE_BITS_OP_STENCIL_FUNC_V_33
#undef CRSTATE_BITS_OP_STENCIL_OP_V_33
        }
        else if (u32Version < SHCROGL_SSM_VERSION_WITH_SPRITE_COORD_ORIGIN)
        {
#define CRSTATE_BITS_OP_VERSION (SHCROGL_SSM_VERSION_WITH_SPRITE_COORD_ORIGIN - 1)
#include "state_bits_globalop.h"
#undef CRSTATE_BITS_OP_VERSION
        }
        else
        {
            /* we do not put dirty bits to state anymore,
             * nop */
//#include "state_bits_globalop.h"
        }
#undef CRSTATE_BITS_OP
        /* always dirty all bits */
        /* return VINF_SUCCESS; */
    }

#define CRSTATE_BITS_OP(_var, _size) FILLDIRTY(pBits->_var);
#include "state_bits_globalop.h"
#undef CRSTATE_BITS_OP
    return VINF_SUCCESS;
}

#define SLC_COPYPTR(ptr) pTmpContext->ptr = pContext->ptr
#define SLC_ASSSERT_NULL_PTR(ptr) CRASSERT(!pContext->ptr)

AssertCompile(VBOXTLSREFDATA_SIZE() <= CR_MAX_BITARRAY);
AssertCompile(VBOXTLSREFDATA_STATE_INITIALIZED != 0);
AssertCompile(RT_OFFSETOF(CRContext, shared) >= VBOXTLSREFDATA_OFFSET(CRContext) + VBOXTLSREFDATA_SIZE() + RT_SIZEOFMEMB(CRContext, bitid) + RT_SIZEOFMEMB(CRContext, neg_bitid));

int32_t crStateLoadContext(CRContext *pContext, CRHashTable * pCtxTable, PFNCRSTATE_CONTEXT_GET pfnCtxGet, PSSMHANDLE pSSM, uint32_t u32Version)
{
    CRContext* pTmpContext;
    int32_t rc, i, j;
    uint32_t uiNumElems, ui, k;
    unsigned long key;
    GLboolean bLoadShared = GL_TRUE;
    GLenum err;

    CRASSERT(pContext && pSSM);

    /* This one is rather big for stack allocation and causes macs to crash */
    pTmpContext = (CRContext*)crAlloc(sizeof(*pTmpContext));
    if (!pTmpContext)
        return VERR_NO_MEMORY;

    CRASSERT(VBoxTlsRefIsFunctional(pContext));

    if (u32Version <= SHCROGL_SSM_VERSION_WITH_INVALID_ERROR_STATE)
    {
        union {
            CRbitvalue bitid[CR_MAX_BITARRAY];
            struct {
                VBOXTLSREFDATA
            } tlsRef;
        } bitid;

        /* do not increment the saved state version due to VBOXTLSREFDATA addition to CRContext */
        rc = SSMR3GetMem(pSSM, pTmpContext, VBOXTLSREFDATA_OFFSET(CRContext));
        AssertRCReturn(rc, rc);

        /* VBox 4.1.8 had a bug that VBOXTLSREFDATA was also stored in the snapshot,
         * thus the saved state data format was changed w/o changing the saved state version.
         * here we determine whether the saved state contains VBOXTLSREFDATA, and if so, treat it accordingly */
        rc = SSMR3GetMem(pSSM, &bitid, sizeof (bitid));
        AssertRCReturn(rc, rc);

        /* the bitid array has one bit set only. this is why if bitid.tlsRef has both cTlsRefs
         * and enmTlsRefState non-zero - this is definitely NOT a bit id and is a VBOXTLSREFDATA */
        if (bitid.tlsRef.enmTlsRefState == VBOXTLSREFDATA_STATE_INITIALIZED
                && bitid.tlsRef.cTlsRefs)
        {
            /* VBOXTLSREFDATA is stored, skip it */
            crMemcpy(&pTmpContext->bitid, ((uint8_t*)&bitid) + VBOXTLSREFDATA_SIZE(), sizeof (bitid) - VBOXTLSREFDATA_SIZE());
            rc = SSMR3GetMem(pSSM, ((uint8_t*)&pTmpContext->bitid) + sizeof (pTmpContext->bitid) - VBOXTLSREFDATA_SIZE(), sizeof (pTmpContext->neg_bitid) + VBOXTLSREFDATA_SIZE());
            AssertRCReturn(rc, rc);

            ui = VBOXTLSREFDATA_OFFSET(CRContext) + VBOXTLSREFDATA_SIZE() + sizeof (pTmpContext->bitid) + sizeof (pTmpContext->neg_bitid);
            ui = RT_OFFSETOF(CRContext, shared) - ui;
        }
        else
        {
            /* VBOXTLSREFDATA is NOT stored */
            crMemcpy(&pTmpContext->bitid, &bitid, sizeof (bitid));
            rc = SSMR3GetMem(pSSM, &pTmpContext->neg_bitid, sizeof (pTmpContext->neg_bitid));
            AssertRCReturn(rc, rc);

            /* the pre-VBOXTLSREFDATA CRContext structure might have additional allignment bits before the CRContext::shared */
            ui = VBOXTLSREFDATA_OFFSET(CRContext) + sizeof (pTmpContext->bitid) + sizeof (pTmpContext->neg_bitid);

            ui &= (sizeof (void*) - 1);
        }

        if (ui)
        {
            void* pTmp = NULL;
            rc = SSMR3GetMem(pSSM, &pTmp, ui);
            AssertRCReturn(rc, rc);
        }

        if (u32Version == SHCROGL_SSM_VERSION_BEFORE_CTXUSAGE_BITS)
        {
            SHCROGL_GET_STRUCT_PART(pTmpContext, CRContext, shared, attrib);
            rc = crStateLoadAttribState_v_BEFORE_CTXUSAGE_BITS(&pTmpContext->attrib, pSSM);
            AssertRCReturn(rc, rc);
            SHCROGL_CUT_FIELD_ALIGNMENT(CRContext, attrib, buffer);
            SHCROGL_GET_STRUCT_PART(pTmpContext, CRContext, buffer, point);
            rc = crStateLoadStencilPoint_v_37(&pTmpContext->point, pSSM);
            AssertRCReturn(rc, rc);
            SHCROGL_GET_STRUCT_PART(pTmpContext, CRContext, polygon, stencil);
            rc = crStateLoadStencilState_v_33(&pTmpContext->stencil, pSSM);
            AssertRCReturn(rc, rc);
            SHCROGL_CUT_FOR_OLD_TYPE_TO_ENSURE_ALIGNMENT(CRContext, stencil, CRStencilState_v_33, sizeof (void*));
            rc = crStateLoadTextureState_v_BEFORE_CTXUSAGE_BITS(&pTmpContext->texture, pSSM);
            AssertRCReturn(rc, rc);
            SHCROGL_CUT_FIELD_ALIGNMENT(CRContext, texture, transform);
            SHCROGL_GET_STRUCT_TAIL(pTmpContext, CRContext, transform);
        }
        else
        {
            SHCROGL_GET_STRUCT_PART(pTmpContext, CRContext, shared, attrib);
            rc = crStateLoadAttribState_v_33(&pTmpContext->attrib, pSSM);
            AssertRCReturn(rc, rc);
            SHCROGL_CUT_FIELD_ALIGNMENT(CRContext, attrib, buffer);
            SHCROGL_GET_STRUCT_PART(pTmpContext, CRContext, buffer, point);
            rc = crStateLoadStencilPoint_v_37(&pTmpContext->point, pSSM);
            AssertRCReturn(rc, rc);
            SHCROGL_GET_STRUCT_PART(pTmpContext, CRContext, polygon, stencil);
            rc = crStateLoadStencilState_v_33(&pTmpContext->stencil, pSSM);
            AssertRCReturn(rc, rc);
            SHCROGL_CUT_FOR_OLD_TYPE_TO_ENSURE_ALIGNMENT(CRContext, stencil, CRStencilState_v_33, sizeof (void*));
            SHCROGL_GET_STRUCT_TAIL(pTmpContext, CRContext, texture);
        }

        pTmpContext->error = GL_NO_ERROR; /* <- the error state contained some random error data here
                                                   * treat as no error */
    }
    else if (u32Version < SHCROGL_SSM_VERSION_WITH_FIXED_STENCIL)
    {
        SHCROGL_GET_STRUCT_HEAD(pTmpContext, CRContext, attrib);
        rc = crStateLoadAttribState_v_33(&pTmpContext->attrib, pSSM);
        AssertRCReturn(rc, rc);
        SHCROGL_CUT_FIELD_ALIGNMENT(CRContext, attrib, buffer);
        SHCROGL_GET_STRUCT_PART(pTmpContext, CRContext, buffer, point);
        rc = crStateLoadStencilPoint_v_37(&pTmpContext->point, pSSM);
        AssertRCReturn(rc, rc);
        SHCROGL_GET_STRUCT_PART(pTmpContext, CRContext, polygon, stencil);
        rc = crStateLoadStencilState_v_33(&pTmpContext->stencil, pSSM);
        AssertRCReturn(rc, rc);
        SHCROGL_CUT_FOR_OLD_TYPE_TO_ENSURE_ALIGNMENT(CRContext, stencil, CRStencilState_v_33, sizeof (void*));
        SHCROGL_GET_STRUCT_TAIL(pTmpContext, CRContext, texture);
    }
    else if (u32Version < SHCROGL_SSM_VERSION_WITH_SPRITE_COORD_ORIGIN)
    {
        SHCROGL_GET_STRUCT_HEAD(pTmpContext, CRContext, point);
        crStateLoadStencilPoint_v_37(&pTmpContext->point, pSSM);
        SHCROGL_GET_STRUCT_TAIL(pTmpContext, CRContext, polygon);
    }
    else
    {
        rc = SSMR3GetMem(pSSM, pTmpContext, sizeof (*pTmpContext));
        AssertRCReturn(rc, rc);
    }

    /* preserve the error to restore it at the end of context creation,
     * it should not normally change, but just in case it it changed */
    err = pTmpContext->error;

    /* we will later do crMemcpy from entire pTmpContext to pContext,
     * for simplicity store the VBOXTLSREFDATA from the pContext to pTmpContext */
    VBOXTLSREFDATA_COPY(pTmpContext, pContext);

    /* Deal with shared state */
    {
        crFindSharedCtxParms_t parms;
        int32_t shared;

        rc = SSMR3GetS32(pSSM, &pContext->shared->id);
        AssertRCReturn(rc, rc);

        rc = SSMR3GetS32(pSSM, &shared);
        AssertRCReturn(rc, rc);

        pTmpContext->shared = NULL;
        parms.pfnCtxGet = pfnCtxGet;
        parms.pSrcCtx = pContext;
        parms.pDstCtx = pTmpContext;
        crHashtableWalk(pCtxTable, crStateFindSharedCB, &parms);

        if (pTmpContext->shared)
        {
            CRASSERT(pContext->shared->refCount==1);
            bLoadShared = GL_FALSE;
            crStateFreeShared(pContext, pContext->shared);
            pContext->shared = NULL;
            pTmpContext->shared->refCount++;
        }
        else
        {
            SLC_COPYPTR(shared);
        }

        if (bLoadShared && shared)
        {
            crStateSetSharedContext(pTmpContext);
        }
    }

    SLC_COPYPTR(flush_func);
    SLC_COPYPTR(flush_arg);

    /* We're supposed to be loading into an empty context, so those pointers should be NULL */
    for ( i = 0 ; i < CR_MAX_ATTRIB_STACK_DEPTH ; i++)
    {
        SLC_ASSSERT_NULL_PTR(attrib.enableStack[i].clip);
        SLC_ASSSERT_NULL_PTR(attrib.enableStack[i].light);

        SLC_ASSSERT_NULL_PTR(attrib.lightingStack[i].light);
        SLC_ASSSERT_NULL_PTR(attrib.transformStack[i].clip);
        SLC_ASSSERT_NULL_PTR(attrib.transformStack[i].clipPlane);

        for (j=0; j<GLEVAL_TOT; ++j)
        {
            SLC_ASSSERT_NULL_PTR(attrib.evalStack[i].eval1D[j].coeff);
            SLC_ASSSERT_NULL_PTR(attrib.evalStack[i].eval2D[j].coeff);
        }
    }

#ifdef CR_ARB_vertex_buffer_object
    SLC_COPYPTR(bufferobject.nullBuffer);
#endif

/*@todo, that should be removed probably as those should hold the offset values, so loading should be fine
  but better check*/
#if 0
#ifdef CR_EXT_compiled_vertex_array
    SLC_COPYPTR(client.array.v.prevPtr);
    SLC_COPYPTR(client.array.c.prevPtr);
    SLC_COPYPTR(client.array.f.prevPtr);
    SLC_COPYPTR(client.array.s.prevPtr);
    SLC_COPYPTR(client.array.e.prevPtr);
    SLC_COPYPTR(client.array.i.prevPtr);
    SLC_COPYPTR(client.array.n.prevPtr);
    for (i = 0 ; i < CR_MAX_TEXTURE_UNITS ; i++)
    {
        SLC_COPYPTR(client.array.t[i].prevPtr);
    }

# ifdef CR_NV_vertex_program
    for (i = 0; i < CR_MAX_VERTEX_ATTRIBS; i++)
    {
        SLC_COPYPTR(client.array.a[i].prevPtr);
    }
# endif
#endif
#endif

#ifdef CR_ARB_vertex_buffer_object
    /*That just sets those pointers to NULL*/
    SLC_COPYPTR(client.array.v.buffer);
    SLC_COPYPTR(client.array.c.buffer);
    SLC_COPYPTR(client.array.f.buffer);
    SLC_COPYPTR(client.array.s.buffer);
    SLC_COPYPTR(client.array.e.buffer);
    SLC_COPYPTR(client.array.i.buffer);
    SLC_COPYPTR(client.array.n.buffer);
    for (i = 0 ; i < CR_MAX_TEXTURE_UNITS ; i++)
    {
        SLC_COPYPTR(client.array.t[i].buffer);
    }
# ifdef CR_NV_vertex_program
    for (i = 0; i < CR_MAX_VERTEX_ATTRIBS; i++)
    {
        SLC_COPYPTR(client.array.a[i].buffer);
    }
# endif
#endif /*CR_ARB_vertex_buffer_object*/

    /*@todo CR_NV_vertex_program*/
    crStateCopyEvalPtrs1D(pTmpContext->eval.eval1D, pContext->eval.eval1D);
    crStateCopyEvalPtrs2D(pTmpContext->eval.eval2D, pContext->eval.eval2D);

    SLC_COPYPTR(feedback.buffer);  /*@todo*/
    SLC_COPYPTR(selection.buffer); /*@todo*/

    SLC_COPYPTR(lighting.light);

    /*This one could be tricky if we're loading snapshot on host with different GPU*/
    SLC_COPYPTR(limits.extensions);

#if CR_ARB_occlusion_query
    SLC_COPYPTR(occlusion.objects); /*@todo*/
#endif

    SLC_COPYPTR(program.errorString);
    SLC_COPYPTR(program.programHash);
    SLC_COPYPTR(program.defaultVertexProgram);
    SLC_COPYPTR(program.defaultFragmentProgram);

    /* Texture pointers */
    for (i=0; i<6; ++i)
    {
        SLC_COPYPTR(texture.base1D.level[i]);
        SLC_COPYPTR(texture.base2D.level[i]);
        SLC_COPYPTR(texture.base3D.level[i]);
        SLC_COPYPTR(texture.proxy1D.level[i]);
        SLC_COPYPTR(texture.proxy2D.level[i]);
        SLC_COPYPTR(texture.proxy3D.level[i]);
#ifdef CR_ARB_texture_cube_map
        SLC_COPYPTR(texture.baseCubeMap.level[i]);
        SLC_COPYPTR(texture.proxyCubeMap.level[i]);
#endif
#ifdef CR_NV_texture_rectangle
        SLC_COPYPTR(texture.baseRect.level[i]);
        SLC_COPYPTR(texture.proxyRect.level[i]);
#endif
    }

    /* Load transform state */
    SLC_COPYPTR(transform.clipPlane);
    SLC_COPYPTR(transform.clip);
    /* Don't have to worry about pContext->transform.current as it'd be set in crStateSetCurrent call */
    /*SLC_COPYPTR(transform.currentStack);*/
    SLC_COPYPTR(transform.modelViewStack.stack);
    SLC_COPYPTR(transform.projectionStack.stack);
    SLC_COPYPTR(transform.colorStack.stack);

    for (i = 0 ; i < CR_MAX_TEXTURE_UNITS ; i++)
    {
        SLC_COPYPTR(transform.textureStack[i].stack);
    }
    for (i = 0 ; i < CR_MAX_PROGRAM_MATRICES ; i++)
    {
        SLC_COPYPTR(transform.programStack[i].stack);
    }

#ifdef CR_OPENGL_VERSION_2_0
    SLC_COPYPTR(glsl.shaders);
    SLC_COPYPTR(glsl.programs);
#endif

    /* Have to preserve original context id */
    CRASSERT(pTmpContext->id == pContext->id);
    CRASSERT(VBOXTLSREFDATA_EQUAL(pContext, pTmpContext));
    /* Copy ordinary state to real context */
    crMemcpy(pContext, pTmpContext, sizeof(*pTmpContext));
    crFree(pTmpContext);
    pTmpContext = NULL;

    /* Now deal with pointers */

    /* Load transform state */
    rc = SSMR3GetMem(pSSM, pContext->transform.clipPlane, sizeof(GLvectord)*CR_MAX_CLIP_PLANES);
    AssertRCReturn(rc, rc);
    rc = SSMR3GetMem(pSSM, pContext->transform.clip, sizeof(GLboolean)*CR_MAX_CLIP_PLANES);
    AssertRCReturn(rc, rc);
    rc = crStateLoadMatrixStack(&pContext->transform.modelViewStack, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadMatrixStack(&pContext->transform.projectionStack, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadMatrixStack(&pContext->transform.colorStack, pSSM);
    AssertRCReturn(rc, rc);
    for (i = 0 ; i < CR_MAX_TEXTURE_UNITS ; i++)
    {
        rc = crStateLoadMatrixStack(&pContext->transform.textureStack[i], pSSM);
        AssertRCReturn(rc, rc);
    }
    for (i = 0 ; i < CR_MAX_PROGRAM_MATRICES ; i++)
    {
        rc = crStateLoadMatrixStack(&pContext->transform.programStack[i], pSSM);
        AssertRCReturn(rc, rc);
    }

    /* Load Textures */
    rc = crStateLoadTextureObjData(&pContext->texture.base1D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjData(&pContext->texture.base2D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjData(&pContext->texture.base3D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjData(&pContext->texture.proxy1D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjData(&pContext->texture.proxy2D, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjData(&pContext->texture.proxy3D, pSSM);
    AssertRCReturn(rc, rc);
#ifdef CR_ARB_texture_cube_map
    rc = crStateLoadTextureObjData(&pContext->texture.baseCubeMap, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjData(&pContext->texture.proxyCubeMap, pSSM);
    AssertRCReturn(rc, rc);
#endif
#ifdef CR_NV_texture_rectangle
    rc = crStateLoadTextureObjData(&pContext->texture.baseRect, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadTextureObjData(&pContext->texture.proxyRect, pSSM);
    AssertRCReturn(rc, rc);
#endif

    if (bLoadShared)
    {
        /* Load shared textures */
        CRASSERT(pContext->shared && pContext->shared->textureTable);

        if (u32Version >= SHCROGL_SSM_VERSION_WITH_ALLOCATED_KEYS)
        {
            rc = crStateLoadKeys(pContext->shared->buffersTable, pSSM, u32Version);
            AssertRCReturn(rc, rc);
        }

        rc = SSMR3GetU32(pSSM, &uiNumElems);
        AssertRCReturn(rc, rc);
        for (ui=0; ui<uiNumElems; ++ui)
        {
            CRTextureObj *pTexture;

            rc = SSMR3GetMem(pSSM, &key, sizeof(key));
            AssertRCReturn(rc, rc);

            pTexture = (CRTextureObj *) crCalloc(sizeof(CRTextureObj));
            if (!pTexture) return VERR_NO_MEMORY;

            rc = crStateLoadTextureObj(pTexture, pSSM, u32Version);
            AssertRCReturn(rc, rc);

            pTexture->hwid = 0;

            /*allocate actual memory*/
            for (i=0; i<6; ++i) {
                pTexture->level[i] = (CRTextureLevel *) crCalloc(sizeof(CRTextureLevel) * CR_MAX_MIPMAP_LEVELS);
                if (!pTexture->level[i]) return VERR_NO_MEMORY;
            }

            rc = crStateLoadTextureObjData(pTexture, pSSM);
            AssertRCReturn(rc, rc);

            crHashtableAdd(pContext->shared->textureTable, key, pTexture);
        }
    }

    /* Load current texture pointers */
    for (i=0; i<CR_MAX_TEXTURE_UNITS; ++i)
    {
        rc = crStateLoadTexUnitCurrentTexturePtrs(&pContext->texture.unit[i], pContext, pSSM);
        AssertRCReturn(rc, rc);
    }

    /* Mark textures for resending to GPU */
    pContext->shared->bTexResyncNeeded = GL_TRUE;

    /* Load lights */
    CRASSERT(pContext->lighting.light);
    rc = SSMR3GetMem(pSSM, pContext->lighting.light, CR_MAX_LIGHTS * sizeof(*pContext->lighting.light));
    AssertRCReturn(rc, rc);

    /* Load attrib stack*/
    for ( i = 0 ; i < CR_MAX_ATTRIB_STACK_DEPTH ; i++)
    {
        if (pContext->attrib.enableStack[i].clip)
        {
            rc = crStateAllocAndSSMR3GetMem(pSSM, (void**)&pContext->attrib.enableStack[i].clip,
                                            pContext->limits.maxClipPlanes*sizeof(GLboolean));
            AssertRCReturn(rc, rc);
        }

        if (pContext->attrib.enableStack[i].light)
        {
            rc = crStateAllocAndSSMR3GetMem(pSSM, (void**)&pContext->attrib.enableStack[i].light,
                                            pContext->limits.maxLights*sizeof(GLboolean));
            AssertRCReturn(rc, rc);
        }

        if (pContext->attrib.lightingStack[i].light)
        {
            rc = crStateAllocAndSSMR3GetMem(pSSM, (void**)&pContext->attrib.lightingStack[i].light,
                                            pContext->limits.maxLights*sizeof(CRLight));
            AssertRCReturn(rc, rc);
        }

        for (k=0; k<pContext->limits.maxTextureUnits; ++k)
        {
            rc = crStateLoadTexUnitCurrentTexturePtrs(&pContext->attrib.textureStack[i].unit[k], pContext, pSSM);
            AssertRCReturn(rc, rc);
        }

        if (pContext->attrib.transformStack[i].clip)
        {
            rc = crStateAllocAndSSMR3GetMem(pSSM, (void*)&pContext->attrib.transformStack[i].clip,
                                            pContext->limits.maxClipPlanes*sizeof(GLboolean));
            AssertRCReturn(rc, rc);
        }

        if (pContext->attrib.transformStack[i].clipPlane)
        {
            rc = crStateAllocAndSSMR3GetMem(pSSM, (void**)&pContext->attrib.transformStack[i].clipPlane,
                                            pContext->limits.maxClipPlanes*sizeof(GLvectord));
            AssertRCReturn(rc, rc);
        }
        rc = crSateLoadEvalCoeffs1D(pContext->attrib.evalStack[i].eval1D, GL_TRUE, pSSM);
        AssertRCReturn(rc, rc);
        rc = crSateLoadEvalCoeffs2D(pContext->attrib.evalStack[i].eval2D, GL_TRUE, pSSM);
        AssertRCReturn(rc, rc);
    }

    /* Load evaluator coeffs */
    rc = crSateLoadEvalCoeffs1D(pContext->eval.eval1D, GL_FALSE, pSSM);
    AssertRCReturn(rc, rc);
    rc = crSateLoadEvalCoeffs2D(pContext->eval.eval2D, GL_FALSE, pSSM);
    AssertRCReturn(rc, rc);

    /* Load buffer objects */
#ifdef CR_ARB_vertex_buffer_object
    if (bLoadShared)
    {
        if (u32Version >= SHCROGL_SSM_VERSION_WITH_ALLOCATED_KEYS)
        {
            rc = crStateLoadKeys(pContext->shared->textureTable, pSSM, u32Version);
            AssertRCReturn(rc, rc);
        }
    }

    rc = SSMR3GetU32(pSSM, &uiNumElems);
    AssertRCReturn(rc, rc);
    for (ui=0; ui<=uiNumElems; ++ui) /*ui<=uiNumElems to load nullBuffer in same loop*/
    {
        CRBufferObject *pBufferObj;

        rc = SSMR3GetMem(pSSM, &key, sizeof(key));
        AssertRCReturn(rc, rc);

        /* default one should be already allocated */
        if (key==0)
        {
            pBufferObj = pContext->bufferobject.nullBuffer;
            if (!pBufferObj) return VERR_SSM_UNEXPECTED_DATA;
        }
        else
        {
            pBufferObj = (CRBufferObject *) crCalloc(sizeof(*pBufferObj));
            if (!pBufferObj) return VERR_NO_MEMORY;
        }

        rc = crStateLoadBufferObject(pBufferObj, pSSM, u32Version);
        AssertRCReturn(rc, rc);

        pBufferObj->hwid = 0;

        if (pBufferObj->data)
        {
            CRASSERT(pBufferObj->size>0);
            pBufferObj->data = crAlloc(pBufferObj->size);
            rc = SSMR3GetMem(pSSM, pBufferObj->data, pBufferObj->size);
            AssertRCReturn(rc, rc);
        }
        else if (pBufferObj->id!=0 && pBufferObj->size>0)
        {
            rc = SSMR3GetMem(pSSM, &pBufferObj->data, sizeof(pBufferObj->data));
            AssertRCReturn(rc, rc);

            if (pBufferObj->data)
            {
                pBufferObj->data = crAlloc(pBufferObj->size);
                rc = SSMR3GetMem(pSSM, pBufferObj->data, pBufferObj->size);
                AssertRCReturn(rc, rc);
            }
        }

        if (key!=0)
            crHashtableAdd(pContext->shared->buffersTable, key, pBufferObj);
    }
    /* Load pointers */
#define CRS_GET_BO(name) (((name)==0) ? (pContext->bufferobject.nullBuffer) : crHashtableSearch(pContext->shared->buffersTable, name))
    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->bufferobject.arrayBuffer = CRS_GET_BO(ui);
    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->bufferobject.elementsBuffer = CRS_GET_BO(ui);
#ifdef CR_ARB_pixel_buffer_object
    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->bufferobject.packBuffer = CRS_GET_BO(ui);
    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->bufferobject.unpackBuffer = CRS_GET_BO(ui);
#endif
#undef CRS_GET_BO

    /* Load client pointers and array buffer bindings*/
    for (i=0; i<CRSTATECLIENT_MAX_VERTEXARRAYS; ++i)
    {
        rc = crStateLoadClientPointer(&pContext->client.array, i, pContext, pSSM);
        AssertRCReturn(rc, rc);
    }
    for (j=0; j<pContext->client.vertexArrayStackDepth; ++j)
    {
        CRVertexArrays *pArray = &pContext->client.vertexArrayStack[j];
        for (i=0; i<CRSTATECLIENT_MAX_VERTEXARRAYS; ++i)
        {
            rc = crStateLoadClientPointer(pArray, i, pContext, pSSM);
            AssertRCReturn(rc, rc);
        }
    }

    pContext->shared->bVBOResyncNeeded = GL_TRUE;
#endif

    /* Load pixel/vertex programs */
    rc = SSMR3GetU32(pSSM, &uiNumElems);
    AssertRCReturn(rc, rc);
    /* Load defaults programs */
    rc = crStateLoadProgram(&pContext->program.defaultVertexProgram, pSSM);
    AssertRCReturn(rc, rc);
    rc = crStateLoadProgram(&pContext->program.defaultFragmentProgram, pSSM);
    AssertRCReturn(rc, rc);
    /* Load all the rest */
    for (ui=0; ui<uiNumElems; ++ui)
    {
        CRProgram *pProgram = NULL;
        rc = crStateLoadProgram(&pProgram, pSSM);
        AssertRCReturn(rc, rc);
        crHashtableAdd(pContext->program.programHash, pProgram->id, pProgram);
        //DIRTY(pProgram->dirtyProgram, pContext->neg_bitid);

    }
    /* Load Pointers */
    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->program.currentVertexProgram = ui==0 ? pContext->program.defaultVertexProgram
                                                     : crHashtableSearch(pContext->program.programHash, ui);
    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->program.currentFragmentProgram = ui==0 ? pContext->program.defaultFragmentProgram
                                                       : crHashtableSearch(pContext->program.programHash, ui);

    /* Mark programs for resending to GPU */
    pContext->program.bResyncNeeded = GL_TRUE;

#ifdef CR_EXT_framebuffer_object
    /* Load FBOs */
    if (bLoadShared)
    {
        if (u32Version >= SHCROGL_SSM_VERSION_WITH_ALLOCATED_KEYS)
        {
            rc = crStateLoadKeys(pContext->shared->fbTable, pSSM, u32Version);
            AssertRCReturn(rc, rc);
        }

        rc = SSMR3GetU32(pSSM, &uiNumElems);
        AssertRCReturn(rc, rc);
        for (ui=0; ui<uiNumElems; ++ui)
        {
            CRFramebufferObject *pFBO;
            pFBO = crAlloc(sizeof(*pFBO));
            if (!pFBO) return VERR_NO_MEMORY;

            rc = SSMR3GetMem(pSSM, &key, sizeof(key));
            AssertRCReturn(rc, rc);

            rc = crStateLoadFramebufferObject(pFBO, pSSM, u32Version);
            AssertRCReturn(rc, rc);

            Assert(key == pFBO->id);

            crHashtableAdd(pContext->shared->fbTable, key, pFBO);
        }

        if (u32Version >= SHCROGL_SSM_VERSION_WITH_ALLOCATED_KEYS)
        {
            rc = crStateLoadKeys(pContext->shared->rbTable, pSSM, u32Version);
            AssertRCReturn(rc, rc);
        }

        rc = SSMR3GetU32(pSSM, &uiNumElems);
        AssertRCReturn(rc, rc);
        for (ui=0; ui<uiNumElems; ++ui)
        {
            CRRenderbufferObject *pRBO;
            pRBO = crAlloc(sizeof(*pRBO));
            if (!pRBO) return VERR_NO_MEMORY;

            rc = SSMR3GetMem(pSSM, &key, sizeof(key));
            AssertRCReturn(rc, rc);

            rc = crStateLoadRenderbufferObject(pRBO, pSSM, u32Version);
            AssertRCReturn(rc, rc);

            crHashtableAdd(pContext->shared->rbTable, key, pRBO);
        }
    }

    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->framebufferobject.drawFB = ui==0 ? NULL
                                               : crHashtableSearch(pContext->shared->fbTable, ui);

    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->framebufferobject.readFB = ui==0 ? NULL
                                               : crHashtableSearch(pContext->shared->fbTable, ui);

    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->framebufferobject.renderbuffer = ui==0 ? NULL
                                                     : crHashtableSearch(pContext->shared->rbTable, ui);

    /* Mark FBOs/RBOs for resending to GPU */
    pContext->shared->bFBOResyncNeeded = GL_TRUE;
#endif

#ifdef CR_OPENGL_VERSION_2_0
    /* Load GLSL related info */
    rc = SSMR3GetU32(pSSM, &uiNumElems);
    AssertRCReturn(rc, rc);

    for (ui=0; ui<uiNumElems; ++ui)
    {
        CRGLSLShader *pShader = crStateLoadGLSLShader(pSSM);
        GLboolean fNewKeyCheck;
        if (!pShader) return VERR_SSM_UNEXPECTED_DATA;
        fNewKeyCheck = crHashtableAllocRegisterKey(pContext->glsl.programs, pShader->id);
        CRASSERT(fNewKeyCheck);
        crHashtableAdd(pContext->glsl.shaders, pShader->id, pShader);
    }

    rc = SSMR3GetU32(pSSM, &uiNumElems);
    AssertRCReturn(rc, rc);

    for (ui=0; ui<uiNumElems; ++ui)
    {
        CRGLSLProgram *pProgram;
        uint32_t numShaders;

        pProgram = crAlloc(sizeof(*pProgram));
        if (!pProgram) return VERR_NO_MEMORY;

        rc = SSMR3GetMem(pSSM, &key, sizeof(key));
        AssertRCReturn(rc, rc);

        rc = SSMR3GetMem(pSSM, pProgram, sizeof(*pProgram));
        AssertRCReturn(rc, rc);

        crHashtableAdd(pContext->glsl.programs, key, pProgram);

        pProgram->currentState.attachedShaders = crAllocHashtable();

        rc = SSMR3GetU32(pSSM, &numShaders);
        AssertRCReturn(rc, rc);

        for (k=0; k<numShaders; ++k)
        {
            rc = SSMR3GetMem(pSSM, &key, sizeof(key));
            AssertRCReturn(rc, rc);
            crHashtableAdd(pProgram->currentState.attachedShaders, key, crHashtableSearch(pContext->glsl.shaders, key));
        }

        if (pProgram->activeState.attachedShaders)
        {
            pProgram->activeState.attachedShaders = crAllocHashtable();

            rc = SSMR3GetU32(pSSM, &numShaders);
            AssertRCReturn(rc, rc);

            for (k=0; k<numShaders; ++k)
            {
                CRGLSLShader *pShader = crStateLoadGLSLShader(pSSM);
                if (!pShader) return VERR_SSM_UNEXPECTED_DATA;
                crHashtableAdd(pProgram->activeState.attachedShaders, pShader->id, pShader);
            }
        }

        if (pProgram->currentState.cAttribs)
            pProgram->currentState.pAttribs = (CRGLSLAttrib*) crAlloc(pProgram->currentState.cAttribs*sizeof(CRGLSLAttrib));
        for (k=0; k<pProgram->currentState.cAttribs; ++k)
        {
            rc = SSMR3GetMem(pSSM, &pProgram->currentState.pAttribs[k].index, sizeof(pProgram->currentState.pAttribs[k].index));
            AssertRCReturn(rc, rc);
            pProgram->currentState.pAttribs[k].name = crStateLoadString(pSSM);
        }

        if (pProgram->activeState.cAttribs)
            pProgram->activeState.pAttribs = (CRGLSLAttrib*) crAlloc(pProgram->activeState.cAttribs*sizeof(CRGLSLAttrib));
        for (k=0; k<pProgram->activeState.cAttribs; ++k)
        {
            rc = SSMR3GetMem(pSSM, &pProgram->activeState.pAttribs[k].index, sizeof(pProgram->activeState.pAttribs[k].index));
            AssertRCReturn(rc, rc);
            pProgram->activeState.pAttribs[k].name = crStateLoadString(pSSM);
        }

        {
            int32_t cUniforms;
            rc = SSMR3GetS32(pSSM, &cUniforms);
            pProgram->cUniforms = cUniforms;
            AssertRCReturn(rc, rc);
        }

        if (pProgram->cUniforms)
        {
            pProgram->pUniforms = crAlloc(pProgram->cUniforms*sizeof(CRGLSLUniform));
            if (!pProgram->pUniforms) return VERR_NO_MEMORY;

            for (k=0; k<pProgram->cUniforms; ++k)
            {
                size_t itemsize, datasize;

                rc = SSMR3GetMem(pSSM, &pProgram->pUniforms[k].type, sizeof(GLenum));
                AssertRCReturn(rc, rc);
                pProgram->pUniforms[k].name = crStateLoadString(pSSM);

                if (crStateIsIntUniform(pProgram->pUniforms[k].type))
                {
                    itemsize = sizeof(GLint);
                } else itemsize = sizeof(GLfloat);

                datasize = crStateGetUniformSize(pProgram->pUniforms[k].type)*itemsize;
                pProgram->pUniforms[k].data = crAlloc(datasize);
                if (!pProgram->pUniforms[k].data) return VERR_NO_MEMORY;

                rc = SSMR3GetMem(pSSM, pProgram->pUniforms[k].data, datasize);
                AssertRCReturn(rc, rc);
            }
        }
    }

    rc = SSMR3GetU32(pSSM, &ui);
    AssertRCReturn(rc, rc);
    pContext->glsl.activeProgram = ui==0 ? NULL
                                         : crHashtableSearch(pContext->glsl.programs, ui);

    /*Mark for resending to GPU*/
    pContext->glsl.bResyncNeeded = GL_TRUE;
#endif

    if (pContext->error != err)
    {
        crWarning("context error state changed on context restore, was 0x%x, but became 0x%x, resetting to its original value",
                err, pContext->error);
        pContext->error = err;
    }

    return VINF_SUCCESS;
}

redpajama_set_name: RedPajamaGithub


RFID protection in all leather wallets is part of the BULLAZO standard. It is the responsibility of every citizen to protect their own personal data. Since all new credit cards are now equipped with an RFID / NFC chip, it will not be long before all cards can be read out. Sensitive credit card and badge data can be copied or stolen. Thanks to the technology, this is now contactless, i. e. without viewing the card number, over a certain distance. With BULLAZO we are open to new trends and adapt our products to new trends. So we didn't hesitate long and integrated RFID & NFC shielding material an RFID blocker into all RFID blocking wallets.
Which one of us leaves the house without money today? The wallet is one of the most widely used small leather goods in Germany. Although the trend of cashless payment is slowly coming to Germany, the trend here is slower than in other countries. And yet the average German has at least 6 cards in his wallet. Particularly when selecting wallets for men, there is therefore a lot to consider, as the men's leather wallet is usually placed directly on the table from the trouser pocket - even in the presence of business partners. The men's leather wallet must look simple and elegant as a daily companion, being classic but not too heavyin the trouser pocket, be hard-wearing and yet extremely high quality. What a catalogue of requirements. Welcome to BULLAZO Wallets for Men! With an individual, discreet engraving of the name or initials, the daily companion becomes a personal unique specimen that can be shown on any table.
At BULLAZO we believe that wallets must be flat and elegant. Today, the gentleman no longer needs a coin pocket because he usually has a pocket for small change. That's why all our leather wallets are for men without a coin pocket. Current wallets trends include the so-called American wallets with money clips or dollar clips. They deliberately waive the coin compartment and make it possible to simply and easily remove the notes from the clip. While some people adore the money clip and thus set style statements, others prefer it more discreetly. This is where our wallet without a coin compartment but with a slip compartment comes in. The well-known banknote compartment is available, but the coin compartment is searched for in vain. Our customers are thrilled. Finally, no thick, uncomfortable wallets are pulled out of the trouser pocket. Small wallets for men are not only an elegant, classic style solution, but also convincing in terms of functionality.


Steve Tolle is president of iConnect Network Services at Merge Healthcare. Follow Merge on Twitter @MergeHealthcare.
Having the right imaging strategy in place is essential not just to providers' bottom lines, but also to the effective care of patients as the industry implements more interconnected healthcare processes.
Medical imaging practices have been doing more with less for longer than most other specialties. In fact, there are many ways in which imaging can serve as a blueprint for other stakeholders as healthcare moves toward value-based and accountable care delivery models.
Imaging is an increasingly important quality indicator, both for healthcare organizations and individual providers. As narrow networks take hold, however, they will fundamentally change the economics for both hospitals and ambulatory facilities that have and focus on image intensive specialties.


text:
Q: XSLT - To remove special character I want to remove special character and space from content,
<litem><para>■ <emph type="bold">Target:</emph> Water</para></litem>
<litem><para>◆ Wound-healing response within the dermis and epidermis via application of heat without creating a traumatic wound</para></litem>

The output would be,
<litem><para><emph type="bold">Target:</emph> Water</para></litem>
<litem><para>Wound-healing response within the dermis and epidermis via application of heat without creating a traumatic wound</para></litem>

I have used this XSLT,
<xsl:output use-character-maps="m1"/>
<xsl:character-map name="m1">
    <xsl:output-character character="■" string=""/>
    <xsl:output-character character="◆" string=""/>
</xsl:character-map>

While using above xslt, only special character is getting remove, not unwanted spce. I want to remove character with space. Could you please guide us.

A: Try <xsl:template match="item/para/text()[1][matches(., '^[■◆]\s*')]"><xsl:value-of select="replace(., '^[■◆]\s*', '')"/></xsl:template> which would only remove any text in the first text() child of para of item if they start with one of those two characters and are followed by optional white space. You might want to adjust the match pattern if you want to have the same effect for other text nodes, but the key is using ^ in the regular expression pattern to indicate to only match the start of a string.

redpajama_set_name: RedPajamaStackExchange