
/****************************************************************************
 *    
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : AbstractNode.c
 *                                                                         
 * Purpose : Handles the intermediate abstract representation of a Vrml
 *          scene graph created by the Parser, which is then used to convert
 *          to a specific form.
 *          
 *          The file contaions methods for AbstractNode's, AbstractFields &
 *          all the basic Vrml field types.
 *                                                                         
 ****************************************************************************/

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

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

#include "rpplugin.h"
#include "abstractnode.h"
#include "vrmlnodetype.h"
#include "route.h"
#include "tokens.h"
#include "symtable.h"

/* Log file stuff */
#include "logfile.h"
#include "utilities.h"
#include "stack.h"

#include "rpvrmlanim.h"
#include "rpvrml.h"
#include "parser.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: abstractnode.c,v 1.44 2001/02/08 12:16:59 johns Exp $";

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

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

typedef             RwBool(*DestroyCallback) (void *);
typedef             RwBool(*PrintCallback) (void *);

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

DestroyCallback     FieldType_GetDestoryCallBack(RwInt32 type);
PrintCallback       FieldType_GetPrintCallBack(RwInt32 type);

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

/* 
 *  used to keep count of refrenced AbstractFields 
 *  & AbstractNodes 
 */
RwInt32             GanCount = 0;
RwInt32             GafCount = 0;

static RwFreeList  *abstractNodeFreeList = (RwFreeList *)NULL;
static RwFreeList  *abstractFieldFreeList = (RwFreeList *)NULL;
static RwFreeList  *abstractEventFreeList = (RwFreeList *)NULL;

typedef struct FieldBlockContext FieldBlockContext;
struct FieldBlockContext
{
    FieldBlock         *boolBlock;
    FieldBlock         *colorBlock;
    FieldBlock         *floatBlock;
    FieldBlock         *imageBlock;
    FieldBlock         *int32Block;
    FieldBlock         *rotationBlock;
    FieldBlock         *stringBlock;
    FieldBlock         *timeBlock;
    FieldBlock         *vec2fBlock;
    FieldBlock         *vec3fBlock;
};

LLStack             currentBlockContextStack; /* of FieldBlockContext */
FieldBlockContext  *currentBlockContext = (FieldBlockContext *)NULL;

static RwBool       FieldBlockContext_Destroy(FieldBlockContext * fbc);

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

   Initialisation/Termination

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

RwBool
AbstractNodeOpen(void)
{
    RWFUNCTION(RWSTRING("AbstractNodeOpen"));

    Stack_Init(&currentBlockContextStack);

    abstractNodeFreeList =
        RwFreeListCreate(sizeof(AbstractNode), 20, 0);
    if (!abstractNodeFreeList)
    {
        RWRETURN(FALSE);
    }

    abstractFieldFreeList =
        RwFreeListCreate(sizeof(AbstractField), 20, 0);
    if (!abstractFieldFreeList)
    {
        RwFreeListDestroy(abstractNodeFreeList);
        abstractNodeFreeList = (RwFreeList *)NULL;
        RWRETURN(FALSE);
    }

    abstractEventFreeList =
        RwFreeListCreate(sizeof(AbstractEvent), 20, 0);
    if (!abstractFieldFreeList)
    {
        RwFreeListDestroy(abstractFieldFreeList);
        abstractFieldFreeList = (RwFreeList *)NULL;
        RwFreeListDestroy(abstractNodeFreeList);
        abstractNodeFreeList = (RwFreeList *)NULL;
        RWRETURN(FALSE);
    }

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

    RWRETURN(TRUE);
}

void
AbstractNodeClose(void)
{
    RWFUNCTION(RWSTRING("AbstractNodeClose"));

    if (abstractEventFreeList)
    {
        RwFreeListDestroy(abstractEventFreeList);
        abstractEventFreeList = (RwFreeList *)NULL;
    }

    if (abstractFieldFreeList)
    {
        RwFreeListDestroy(abstractFieldFreeList);
        abstractFieldFreeList = (RwFreeList *)NULL;
    }

    if (abstractNodeFreeList)
    {
        RwFreeListDestroy(abstractNodeFreeList);
        abstractNodeFreeList = (RwFreeList *)NULL;
    }

    AbstractNodePopBlockContext();

    RWRETURNVOID();
}

static FieldBlockContext *
FieldBlockContext_Create(void)
{
    FieldBlockContext  *fbc;

    RWFUNCTION(RWSTRING("FieldBlockContext_Create"));

    fbc = (FieldBlockContext *) RwMalloc(sizeof(FieldBlockContext));
    if (!fbc)
    {
        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->boolBlock = FieldBlock_Create(sizeof(sfbool), 10);
    if (!fbc->boolBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->colorBlock = FieldBlock_Create(sizeof(sfcolor), 40);
    if (!fbc->colorBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->floatBlock = FieldBlock_Create(sizeof(sffloat), 40);
    if (!fbc->floatBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->imageBlock = FieldBlock_Create(sizeof(sfimage), 10);
    if (!fbc->imageBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->int32Block = FieldBlock_Create(sizeof(sfint32), 40);
    if (!fbc->int32Block)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->rotationBlock = FieldBlock_Create(sizeof(sfrotation), 40);
    if (!fbc->rotationBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->stringBlock = FieldBlock_Create(sizeof(sfstring), 40);
    if (!fbc->stringBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->timeBlock = FieldBlock_Create(sizeof(sftime), 10);
    if (!fbc->timeBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->vec2fBlock = FieldBlock_Create(sizeof(sfvec2f), 10);
    if (!fbc->vec2fBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    fbc->vec3fBlock = FieldBlock_Create(sizeof(sfvec3f), 40);
    if (!fbc->vec3fBlock)
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN((FieldBlockContext *)NULL);
    }

    RWRETURN(fbc);
}

static              RwBool
FieldBlockContext_Destroy(FieldBlockContext * fbc)
{
    RWFUNCTION(RWSTRING("FieldBlockContext_Destroy"));
    RWASSERT(fbc);

    if (fbc)
    {
        if (fbc->boolBlock)
        {
            FieldBlock_Destroy(fbc->boolBlock);
        }

        if (fbc->colorBlock)
        {
            FieldBlock_Destroy(fbc->colorBlock);
        }

        if (fbc->floatBlock)
        {
            FieldBlock_Destroy(fbc->floatBlock);
        }

        if (fbc->imageBlock)
        {
            FieldBlock_Destroy(fbc->imageBlock);
        }

        if (fbc->int32Block)
        {
            FieldBlock_Destroy(fbc->int32Block);
        }

        if (fbc->rotationBlock)
        {
            FieldBlock_Destroy(fbc->rotationBlock);
        }

        if (fbc->stringBlock)
        {
            FieldBlock_Destroy(fbc->stringBlock);
        }

        if (fbc->timeBlock)
        {
            FieldBlock_Destroy(fbc->timeBlock);
        }

        if (fbc->vec2fBlock)
        {
            FieldBlock_Destroy(fbc->vec2fBlock);
        }

        if (fbc->vec3fBlock)
        {
            FieldBlock_Destroy(fbc->vec3fBlock);
        }

        RwFree(fbc);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
AbstractNodePushBlockContext(void)
{
    FieldBlockContext  *fbc;

    RWFUNCTION(RWSTRING("AbstractNodePushBlockContext"));

    fbc = FieldBlockContext_Create();
    if (!fbc)
    {
        RWRETURN(FALSE);
    }

    if (!Stack_Push(&currentBlockContextStack, fbc))
    {
        FieldBlockContext_Destroy(fbc);

        RWRETURN(FALSE);
    }

    currentBlockContext = fbc;

    RWRETURN(TRUE);
}

RwBool
AbstractNodePopBlockContext(void)
{
    FieldBlockContext  *fbc;

    RWFUNCTION(RWSTRING("AbstractNodePopBlockContext"));

    fbc = (FieldBlockContext *) Stack_Pop(&currentBlockContextStack);
    if (!fbc)
    {
        RWRETURN(FALSE);
    }

    FieldBlockContext_Destroy(fbc);

    fbc = (FieldBlockContext *) Stack_Top(&currentBlockContextStack);
    currentBlockContext = fbc;

    RWRETURN(TRUE);
}

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

   AbstractNode methods

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

AbstractNode       *
AbstractNode_Create(const RwChar * nm)
{
    RWFUNCTION(RWSTRING("AbstractNode_Create"));
    RWASSERT(nm);

    if (nm)
    {
        AbstractNode       *an;

        an = (AbstractNode *) RwFreeListAlloc(abstractNodeFreeList);
        if (!an)
        {
            RWRETURN((AbstractNode *)NULL);
        }

        rwstrdup(an->name, nm);
        if (!an->name)
        {
            AbstractNode_Destroy(an);

            RWRETURN((AbstractNode *)NULL);
        }

        an->symName = (RwChar *)NULL;

        an->refCnt = 0;
        an->userProto = FALSE;
        LLinkList_Init(&an->fields);
        LLinkList_Init(&an->eventIns);
        LLinkList_Init(&an->eventOuts);

        GanCount++;

        RWRETURN(an);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

RwBool
AbstractNode_Destroy(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("AbstractNode_Destroy"));
    RWASSERT(an);

    if (an)
    {
        /* last refrence or never been refrenced at all */
        if ((an->refCnt == 1) || (an->refCnt == 0))
        {
            /* Free the names */
            if (an->name)
            {
                RwFree(an->name);
            }

            if (an->symName)
            {
                RwFree(an->symName);
            }

            /* destroy fields list */
            LLinkList_Destroy(&an->fields,
                              (linkCallback) AbstractField_Destroy);
            LLinkList_Destroy(&an->eventIns,
                              (linkCallback) AbstractEvent_Destroy);
            LLinkList_Destroy(&an->eventOuts,
                              (linkCallback) AbstractEvent_Destroy);

            RwFreeListFree(abstractNodeFreeList, an);

            GanCount--;
        }
        else
        {
            an->refCnt--;
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

AbstractNode       *
AbstractNode_Copy(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("AbstractNode_Copy"));
    RWASSERT(an);

    if (an)
    {
        AbstractNode       *anNew;
        AbstractField      *af;

        anNew = AbstractNode_Create(an->name);
        if (!anNew)
        {
            RWRETURN((AbstractNode *)NULL);
        }

        LLinkList_IteratorReset(&an->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorNext(&an->fields)))
        {
            AbstractField      *afNew;

            afNew = AbstractField_Copy(af, af->name);
            LLinkList_AddData(&anNew->fields, afNew);
        }

        anNew->refCnt = 1;
        anNew->lineNum = an->lineNum;
        anNew->userProto = an->userProto;

        RWRETURN(anNew);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

AbstractNode       *
AbstractNode_AddRef(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("AbstractNode_AddRef"));
    RWASSERT(an);

    if (an)
    {
        an->refCnt++;

        RWRETURN(an);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

static              RwBool
Field_Print(Field * field, linkCallback printCB)
{
    RWFUNCTION(RWSTRING("Field_Print"));
    RWASSERT(field);
    RWASSERT(printCB);

    if (field && printCB)
    {
        RwInt32             numElements, i;

        numElements = Field_NumElements(field);
        for (i = 0; i < numElements; i++)
        {
            void               *element;

            element = Field_GetValue(field, i);
            printCB(element);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
AbstractNode_Print(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("AbstractNode_Print"));
    RWASSERT(an);

    if (an)
    {
        AbstractField      *af;

        LOGMSG(("%s\n", an->name));
        LOGMSG(("%c\n", '{'));
        LOGINCINDENT();

        LLinkList_IteratorReset(&an->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorNext(&an->fields)))
        {
            Field              *field;
            PrintCallback       printCB;

            field = AbstractField_GetField(af);

            LOGMSG((af->name));
            LOGMSG((" [%d]", field->type));

            printCB = FieldType_GetPrintCallBack(field->type);
            if (printCB)
            {
                Field_Print(field, (linkCallback) printCB);
            }

            LOGMSG(("\n"));
        }

        LOGDECINDENT();
        LOGMSG(("%c\n", '}'));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

AbstractNode       *
AbstractNodeTree_InstanceProto(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("AbstractNodeTree_InstanceProto"));
    RWASSERT(an);

    if (an)
    {
        AbstractField      *af;

        if (an->userProto)
        {
            AbstractNode       *anInstance;

            anInstance = instanceUserProto(an);

            RWRETURN(anInstance);
        }
        else
        {
            LLinkList_IteratorReset(&an->fields);
            while ((af =
                    (AbstractField *) LLinkList_IteratorNext(&an->
                                                             fields)))
            {
                Field              *field;

                field = AbstractField_GetField(af);
                if ((field->type == MFNODE) || (field->type == SFNODE))
                {
                    RwInt32             i;

                    for (i = 0; i < field->items; i++)
                    {

                        AbstractNode       *anChild;
                        AbstractNode       *anInstance;

                        anChild = FieldMfnode_GetValue(field, i);
                        anInstance =
                            AbstractNodeTree_InstanceProto(anChild);

                        if (anInstance != anChild)
                        {
                            /* patch it in to the node tree */
                            (*field->fieldArray).sfnode = anInstance;
                        }
                    }
                }
            }
        }

        RWRETURN(an);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

AbstractNode       *
AbstractNode_GetChildNode(AbstractNode * an, const RwChar * name)
{
    RWFUNCTION(RWSTRING("AbstractNode_GetChildNode"));
    RWASSERT(an);
    RWASSERT(name);

    if (an && name)
    {
        AbstractField      *af;

        LLinkList_IteratorReset(&an->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorNext(&an->fields)))
        {
            if ((af->type == SFNODE) && (rwstrcmp(af->name, name) == 0))
            {
                Field              *field;
                RwInt32             numItems;

                field = AbstractField_GetField(af);
                numItems = Field_NumElements(field);

                if (numItems > 0)
                {
                    AbstractNode       *anChild;

                    anChild = FieldSfnode_GetValue(field);

                    RWRETURN(anChild);
                }

                RWRETURN((AbstractNode *)NULL);
            }
        }

        RWRETURN((AbstractNode *)NULL);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

AbstractField      *
AbstractNode_GetAbstractField(AbstractNode * an, const RwChar * name)
{
    RWFUNCTION(RWSTRING("AbstractNode_GetAbstractField"));
    RWASSERT(an);
    RWASSERT(name);

    if (an && name)
    {
        AbstractField      *af;
        VrmlNodeType       *t;
        LLLink             *iterator;

        iterator = LLinkList_IteratorReset(&an->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorPrev(&an->fields)))
        {
            if (rwstrcmp(af->name, name) == 0)
            {
                if (af->Items > 0)
                {
                    RWRETURN(af);
                }
                else
                {
                    RWRETURN((AbstractField *)NULL);
                }
            }
        }
        LLinkList_IteratorRestore(&an->fields, iterator);

        /* else get default value */
        t = TypeList_find(an->name);
        if (!t)
        {
            RWRETURN((AbstractField *)NULL);
        }

        iterator = LLinkList_IteratorReset(&t->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorPrev(&t->fields)))
        {
            if (rwstrcmp(af->name, name) == 0)
            {
                if (af->Items > 0)
                {
                    RWRETURN(af);
                }
                else
                {
                    RWRETURN((AbstractField *)NULL);
                }
            }
        }
        LLinkList_IteratorRestore(&t->fields, iterator);

        RWRETURN((AbstractField *)NULL);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractField *)NULL);
}

AbstractEvent      *
AbstractNode_GetAbstractEventIn(AbstractNode * an, const RwChar * name)
{
    RWFUNCTION(RWSTRING("AbstractNode_GetAbstractEventIn"));
    RWASSERT(an);
    RWASSERT(name);

    if (an && name)
    {
        AbstractEvent      *ae;

        LLinkList_IteratorReset(&an->eventIns);
        while ((ae =
                (AbstractEvent *) LLinkList_IteratorNext(&an->
                                                         eventIns)))
        {
            if (rwstrcmp(ae->name, name) == 0)
            {
                if (!LLinkList_IsEmpty(&ae->eventRecList))
                {
                    RWRETURN(ae);
                }
                else
                {
                    RWRETURN((AbstractEvent *)NULL);
                }
            }
        }

        RWRETURN((AbstractEvent *)NULL);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractEvent *)NULL);
}

AbstractEvent      *
AbstractNode_GetAbstractEventOut(AbstractNode * an, const RwChar * name)
{
    RWFUNCTION(RWSTRING("AbstractNode_GetAbstractEventOut"));
    RWASSERT(an);
    RWASSERT(name);

    if (an && name)
    {
        AbstractEvent      *ae;

        LLinkList_IteratorReset(&an->eventOuts);
        while ((ae =
                (AbstractEvent *) LLinkList_IteratorNext(&an->
                                                         eventOuts)))
        {
            if (rwstrcmp(ae->name, name) == 0)
            {
                if (!LLinkList_IsEmpty(&ae->eventRecList))
                {
                    RWRETURN(ae);
                }
                else
                {
                    RWRETURN((AbstractEvent *)NULL);
                }
            }
        }

        RWRETURN((AbstractEvent *)NULL);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractEvent *)NULL);
}

RwChar             *
AbstractNode_GetName(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("AbstractNode_GetName"));
    RWASSERT(an);

    if (an)
    {
        RWRETURN(an->name);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((char *)NULL);
}

RwChar             *
AbstractNode_GetSymName(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("AbstractNode_GetSymName"));
    RWASSERT(an);

    if (an)
    {
        RWRETURN(an->symName);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((char *)NULL);
}

RwChar             *
AbstractNode_SetSymName(AbstractNode * an, const RwChar * sn)
{
    RWFUNCTION(RWSTRING("AbstractNode_SetSymName"));
    RWASSERT(an);

    if (an)
    {
        rwstrdup(an->symName, sn);

        RWRETURN(an->symName);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((char *)NULL);
}

RwChar             *
AbstractNode_GetBaseName(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("AbstractNode_GetBaseName"));
    RWASSERT(an);

    if (an)
    {
        if (an->userProto)
        {
            VrmlNodeType       *t;
            AbstractNode       *anBase;

            t = TypeList_find(an->name);
            if (!t)
            {
                RWRETURN((char *)NULL);
            }

            anBase = VrmlNodeType_GetBaseAbstractNode(t);
            if (!anBase)
            {
                RWRETURN((char *)NULL);
            }

            RWRETURN(anBase->name);
        }
        else
        {
            RWRETURN(an->name);
        }
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((char *)NULL);
}

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

   AbstractField methods

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

Field              *
AbstractField_GetField(AbstractField * af)
{
    RWFUNCTION(RWSTRING("AbstractField_GetField"));
    RWASSERT(af);

    if (af)
    {
        RWRETURN(af->field);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((Field *)NULL);
}

AbstractField      *
AbstractField_Create(const RwChar * nm, RwInt32 type)
{
    RWFUNCTION(RWSTRING("AbstractField_Create"));
    RWASSERT(nm);

    if (nm)
    {
        AbstractField      *af;

        af = (AbstractField *) RwFreeListAlloc(abstractFieldFreeList);
        if (!af)
        {
            RWRETURN((AbstractField *)NULL);
        }

        rwstrdup(af->name, nm);
        if (!af->name)
        {
            AbstractField_Destroy(af);

            RWRETURN((AbstractField *)NULL);
        }

        af->refCnt = 1;
        af->type = type;
        af->Items = 0;

        af->field = Field_Create(type);
        if (!af->field)
        {
            AbstractField_Destroy(af);

            RWRETURN((AbstractField *)NULL);
        }

        GafCount++;

        RWRETURN(af);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractField *)NULL);
}

RwBool
AbstractField_Destroy(AbstractField * af)
{
    RWFUNCTION(RWSTRING("AbstractField_Destroy"));
    RWASSERT(af);

    if (af)
    {
        if (af->refCnt == 1)
        {
            Field              *field;

            /* Free the name (allocated by rwstrdup) */
            RwFree(af->name);

            /* deftroy the Field */
            field = AbstractField_GetField(af);
            Field_Destroy(field);

            RwFreeListFree(abstractFieldFreeList, af);

            GafCount--;
        }
        else
        {
            af->refCnt--;
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

AbstractField      *
AbstractField_AddRef(AbstractField * af)
{
    RWFUNCTION(RWSTRING("AbstractField_AddRef"));
    RWASSERT(af);

    if (af)
    {
        af->refCnt++;

        RWRETURN(af);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

AbstractField      *
AbstractField_Copy(AbstractField * af, const RwChar * newName)
{
    RWFUNCTION(RWSTRING("AbstractField_Copy"));
    RWASSERT(af);
    RWASSERT(newName);

    if (af && newName)
    {
        AbstractField      *newAf = (AbstractField *)NULL;

        newAf = AbstractField_Create(newName, af->type);
        if (!newAf)
        {
            RWRETURN((AbstractField *)NULL);
        }

        /* NEW STUFF */
        newAf->Items = af->Items;
        Field_Destroy(newAf->field);
        newAf->field = Field_AddRef(af->field);

        RWRETURN(newAf);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   Field methods

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

Field              *
Field_Create(RwInt32 type)
{
    Field              *field;

    RWFUNCTION(RWSTRING("Field_Create"));

    field = (Field *) RwMalloc(sizeof(Field));
    if (!field)
    {
        RWRETURN((Field *)NULL);
    }

    field->type = type;
    field->refCnt = 1;
    field->items = 0;
    field->elementSize = 0;
    field->fieldArray = (sfAny *)NULL;

    RWRETURN(field);
}

RwBool
Field_Destroy(Field * field)
{
    RWFUNCTION(RWSTRING("Field_Destroy"));
    RWASSERT(field);

    if (field)
    {
        if (field->refCnt == 1)
        {
            DestroyCallback     destroyCB;

            destroyCB = FieldType_GetDestoryCallBack(field->type);
            if (destroyCB)
            {
                RwInt32             numElements, i;

                numElements = Field_NumElements(field);
                for (i = 0; i < numElements; i++)
                {
                    void               *element;

                    element = Field_GetValue(field, i);
                    destroyCB(element);
                }
            }

            RwFree(field->fieldArray);
            RwFree(field);

            RWRETURN(TRUE);
        }
        else
        {
            field->refCnt--;
        }
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

Field              *
Field_AddRef(Field * field)
{
    RWFUNCTION(RWSTRING("Field_AddRef"));
    RWASSERT(field);

    if (field)
    {
        field->refCnt++;

        RWRETURN(field);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((Field *)NULL);
}

RwBool
Field_InitFormFieldList(Field * field, LLinkList * fieldValueList)
{
    RWFUNCTION(RWSTRING("Field_InitFormFieldList"));
    RWASSERT(field);
    RWASSERT(fieldValueList);

    if (field && fieldValueList)
    {
        RwInt32             items;
        sfAny              *fieldArray;
        void               *element;

        items = LLinkList_NumItems(fieldValueList);
        field->fieldArray = fieldArray = (sfAny *)
            RwMalloc(items * sizeof(sfAny));
        if (!field->fieldArray)
        {
            RWRETURN(FALSE);
        }
        field->items = items;

        LLinkList_IteratorReset(fieldValueList);
        while ((element = LLinkList_IteratorPrev(fieldValueList)))
        {
            fieldArray->element = element;
            fieldArray++;
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwInt32
Field_NumElements(Field * field)
{
    RWFUNCTION(RWSTRING("Field_NumElements"));
    RWASSERT(field);

    if (field)
    {
        RWRETURN(field->items);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(-1);
}

DestroyCallback
FieldType_GetDestoryCallBack(RwInt32 type)
{
    DestroyCallback     destroyCB = (DestroyCallback)NULL;

    RWFUNCTION(RWSTRING("FieldType_GetDestoryCallBack"));

    switch (type)
    {
        case SFVRMLBool:
            {
                destroyCB = (DestroyCallback) sfbool_Destroy;
                break;
            }

        case SFCOLOR:
        case MFCOLOR:
            {
                destroyCB = (DestroyCallback) sfcolor_Destroy;
                break;
            }

        case SFFLOAT:
        case MFFLOAT:
            {
                destroyCB = (DestroyCallback) sffloat_Destroy;
                break;
            }

        case SFINT32:
        case MFINT32:
            {
                destroyCB = (DestroyCallback) sfint32_Destroy;
                break;
            }

        case SFIMAGE:
            {
                destroyCB = (DestroyCallback) sfimage_Destroy;
                break;
            }

        case SFROTATION:
        case MFROTATION:
            {
                destroyCB = (DestroyCallback) sfrotation_Destroy;
                break;
            }

        case SFSTRING:
        case MFSTRING:
            {
                destroyCB = (DestroyCallback) sfstring_Destroy;
                break;
            }

        case SFTIME:
            {
                destroyCB = (DestroyCallback) sftime_Destroy;
                break;
            }

        case SFVEC2F:
        case MFVEC2F:
            {
                destroyCB = (DestroyCallback) sfvec2f_Destroy;
                break;
            }

        case SFVEC3F:
        case MFVEC3F:
            {
                destroyCB = (DestroyCallback) sfvec3f_Destroy;
                break;
            }

        case SFNODE:
        case MFNODE:
            {
                destroyCB = (DestroyCallback) AbstractNode_Destroy;
                break;
            }
    }

    RWRETURN(destroyCB);
}

PrintCallback
FieldType_GetPrintCallBack(RwInt32 type)
{
    PrintCallback       printCB = (PrintCallback)NULL;

    RWFUNCTION(RWSTRING("FieldType_GetPrintCallBack"));

    switch (type)
    {
        case SFVRMLBool:
            {
                printCB = (PrintCallback) sfbool_Print;
                break;
            }

        case SFCOLOR:
        case MFCOLOR:
            {
                printCB = (PrintCallback) sfcolor_Print;
                break;
            }

        case SFFLOAT:
        case MFFLOAT:
            {
                printCB = (PrintCallback) sffloat_Print;
                break;
            }

        case SFINT32:
        case MFINT32:
            {
                printCB = (PrintCallback) sfint32_Print;
                break;
            }

        case SFIMAGE:
            {
                printCB = (PrintCallback) sfimage_Print;
                break;
            }

        case SFROTATION:
        case MFROTATION:
            {
                printCB = (PrintCallback) sfrotation_Print;
                break;
            }

        case SFSTRING:
        case MFSTRING:
            {
                printCB = (PrintCallback) sfstring_Print;
                break;
            }

        case SFTIME:
            {
                printCB = (PrintCallback) sftime_Print;
                break;
            }

        case SFVEC2F:
        case MFVEC2F:
            {
                printCB = (PrintCallback) sfvec2f_Print;
                break;
            }

        case SFVEC3F:
        case MFVEC3F:
            {
                printCB = (PrintCallback) sfvec3f_Print;
                break;
            }

        case SFNODE:
        case MFNODE:
            {
                printCB = (PrintCallback) AbstractNode_Print;
                break;
            }
    }

    RWRETURN(printCB);
}

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

   AbstractEvent methods

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

AbstractEvent      *
AbstractEvent_Create(const RwChar * name, RwInt32 type)
{
    RWFUNCTION(RWSTRING("AbstractEvent_Create"));
    RWASSERT(name);

    if (name)
    {
        AbstractEvent      *ae;

        ae = (AbstractEvent *) RwFreeListAlloc(abstractEventFreeList);
        if (!ae)
        {
            RWRETURN((AbstractEvent *)NULL);
        }

        rwstrdup(ae->name, name);
        if (!ae->name)
        {
            AbstractEvent_Destroy(ae);

            RWRETURN((AbstractEvent *)NULL);
        }

        ae->type = type;
        LLinkList_Init(&ae->eventRecList);

        RWRETURN(ae);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractEvent *)NULL);
}

RwBool
AbstractEvent_Destroy(AbstractEvent * ae)
{
    RWFUNCTION(RWSTRING("AbstractEvent_Destroy"));
    RWASSERT(ae);

    if (ae)
    {
        if (ae->name)
        {
            RwFree(ae->name);
        }

        LLinkList_Destroy(&ae->eventRecList,
                          (linkCallback) Route_DestroyRecord);
        RwFreeListFree(abstractEventFreeList, ae);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

AbstractEvent      *
AbstractEvent_Copy(AbstractEvent * ae)
{
    RWFUNCTION(RWSTRING("AbstractEvent_Copy"));
    RWASSERT(ae);

    if (ae)
    {
        AbstractEvent      *aeNew;
        RouteRec           *routeRec;

        aeNew = AbstractEvent_Create(ae->name, ae->type);
        if (!aeNew)
        {
            RWRETURN((AbstractEvent *)NULL);
        }

        LLinkList_IteratorReset(&ae->eventRecList);
        while ((routeRec =
                (RouteRec *) LLinkList_IteratorNext(&ae->eventRecList)))
        {
            RouteRec           *routeRecNew;

            routeRecNew =
                Route_CreateRecord(routeRec->toNode, routeRec->toEvent);
            if (!routeRecNew)
            {
                AbstractEvent_Destroy(aeNew);

                RWRETURN((AbstractEvent *)NULL);
            }

            if (!LLinkList_AddData(&aeNew->eventRecList, routeRecNew))
            {
                Route_DestroyRecord(routeRecNew);
                AbstractEvent_Destroy(aeNew);

                RWRETURN((AbstractEvent *)NULL);
            }
        }

        RWRETURN(aeNew);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractEvent *)NULL);
}

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

   sfbool methods

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

sfbool             *
sfbool_Create(void)
{
    sfbool             *sfb;

    RWFUNCTION(RWSTRING("sfbool_Create"));

    sfb =
        (sfbool *) FieldBlock_GetElement(currentBlockContext->
                                         boolBlock);
    if (!sfb)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfbool)));
        RWRETURN((RwInt32 *)NULL);
    }

    *sfb = 0;

    RWRETURN(sfb);
}

RwBool
sfbool_Destroy(sfbool * sfb)
{
    RWFUNCTION(RWSTRING("sfbool_Destroy"));
    RWASSERT(sfb);

    if (sfb)
    {
        FieldBlock_ReleaseElement(currentBlockContext->boolBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfbool_Print(sfbool * sfb)
{
    RWFUNCTION(RWSTRING("sfbool_Print"));
    RWASSERT(sfb);

    if (sfb)
    {
        if (*sfb)
        {
            LOGMSG(("TRUE"));
        }
        else
        {
            LOGMSG(("FALSE"));
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sfcolor methods

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

sfcolor            *
sfcolor_Create(void)
{
    sfcolor            *sfc;

    RWFUNCTION(RWSTRING("sfcolor_Create"));

    sfc =
        (sfcolor *) FieldBlock_GetElement(currentBlockContext->
                                          colorBlock);
    if (!sfc)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfcolor)));
        RWRETURN((sfcolor *)NULL);
    }

    sfc->r = 0.0f;
    sfc->g = 0.0f;
    sfc->b = 0.0f;

    RWRETURN(sfc);
}

RwBool
sfcolor_Destroy(sfcolor * sfc)
{
    RWFUNCTION(RWSTRING("sfcolor_Destroy"));
    RWASSERT(sfc);

    if (sfc)
    {
        FieldBlock_ReleaseElement(currentBlockContext->colorBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfcolor_Print(sfcolor * sfc)
{
    RWFUNCTION(RWSTRING("sfcolor_Print"));
    RWASSERT(sfc);

    if (sfc)
    {
        LOGMSG(("%f, %f, %f", sfc->r, sfc->g, sfc->b));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfcolor_Compare(const sfcolor * sfcA, const sfcolor * sfcB)
{
    RWFUNCTION(RWSTRING("sfcolor_Compare"));
    RWASSERT(sfcA);
    RWASSERT(sfcB);

    if (sfcA && sfcB)
    {
        if ((sfcA->r == sfcB->r) &&
            (sfcA->g == sfcB->g) && (sfcA->b == sfcB->b))
        {
            RWRETURN(TRUE);
        }

        RWRETURN(FALSE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sffloat methods

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

sffloat            *
sffloat_Create(void)
{
    sffloat            *sff;

    RWFUNCTION(RWSTRING("sffloat_Create"));

    sff =
        (sffloat *) FieldBlock_GetElement(currentBlockContext->
                                          floatBlock);
    if (!sff)
    {
        RWERROR((E_RW_NOMEM, sizeof(sffloat)));
        RWRETURN((float *)NULL);
    }

    *sff = 0.0f;

    RWRETURN(sff);
}

RwBool
sffloat_Destroy(sffloat * sff)
{
    RWFUNCTION(RWSTRING("sffloat_Destroy"));
    RWASSERT(sff);

    if (sff)
    {
        FieldBlock_ReleaseElement(currentBlockContext->floatBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sffloat_Print(sffloat * sff)
{
    RWFUNCTION(RWSTRING("sffloat_Print"));
    RWASSERT(sff);

    if (sff)
    {
        LOGMSG(("%f", *sff));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sfimage methods

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

sfimage            *
sfimage_Create(void)
{
    sfimage            *sfim;

    RWFUNCTION(RWSTRING("sfimage_Create"));

    sfim =
        (sfimage *) FieldBlock_GetElement(currentBlockContext->
                                          imageBlock);
    if (!sfim)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfimage)));
        RWRETURN((sfimage *)NULL);
    }

    sfim->width = 0;
    sfim->height = 0;
    sfim->components = 0;
    sfim->pixels = (unsigned char *)NULL;

    RWRETURN(sfim);
}

RwBool
sfimage_Destroy(sfimage * sfim)
{
    RWFUNCTION(RWSTRING("sfimage_Destroy"));
    RWASSERT(sfim);

    if (sfim)
    {
        if (sfim->pixels)
        {
            RwFree(sfim->pixels);
        }

        FieldBlock_ReleaseElement(currentBlockContext->imageBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfimage_Print(sfimage * sfim)
{
    RWFUNCTION(RWSTRING("sfimage_Print"));
    RWASSERT(sfim);

    if (sfim)
    {
        RwInt32             pixelNum;
        RwUInt16            w, h;
        char                c;

        w = sfim->width;
        h = sfim->height;
        c = sfim->components;
        LOGMSG(("%d, %d, %d", w, h, c));
        for (pixelNum = 0; pixelNum < (w * h * c); pixelNum++)
        {
            LOGMSG((", %d", sfim->pixels[pixelNum]));
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sfint32 methods

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

sfint32            *
sfint32_Create(void)
{
    sfint32            *sfi;

    RWFUNCTION(RWSTRING("sfint32_Create"));

    sfi =
        (sfint32 *) FieldBlock_GetElement(currentBlockContext->
                                          int32Block);
    if (!sfi)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfimage)));
        RWRETURN((RwInt32 *)NULL);
    }

    *sfi = 0;

    RWRETURN(sfi);
}

RwBool
sfint32_Destroy(sfint32 * sfi)
{
    RWFUNCTION(RWSTRING("sfint32_Destroy"));
    RWASSERT(sfi);

    if (sfi)
    {
        FieldBlock_ReleaseElement(currentBlockContext->int32Block);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfint32_Print(sfint32 * sfi)
{
    RWFUNCTION(RWSTRING("sfint32_Print"));
    RWASSERT(sfi);

    if (sfi)
    {
        LOGMSG((" %d, ", *sfi));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sfrotation methods

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

sfrotation         *
sfrotation_Create(void)
{
    sfrotation         *sfr;

    RWFUNCTION(RWSTRING("sfrotation_Create"));

    sfr = (sfrotation *)
        FieldBlock_GetElement(currentBlockContext->rotationBlock);
    if (!sfr)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfimage)));
        RWRETURN((sfrotation *)NULL);
    }

    sfr->x = 1.0f;
    sfr->y = 0.0f;
    sfr->z = 0.0f;
    sfr->angle = 0.0f;

    RWRETURN(sfr);
}

RwBool
sfrotation_Destroy(sfrotation * sfr)
{
    RWFUNCTION(RWSTRING("sfrotation_Destroy"));
    RWASSERT(sfr);

    if (sfr)
    {
        FieldBlock_ReleaseElement(currentBlockContext->rotationBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfrotation_Print(sfrotation * sfr)
{
    RWFUNCTION(RWSTRING("sfrotation_Print"));
    RWASSERT(sfr);

    if (sfr)
    {
        LOGMSG(("%f %f %f %f", sfr->x, sfr->y, sfr->z, sfr->angle));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sfstring methods

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

sfstring           *
sfstring_Create(void)
{
    sfstring           *sfs;

    RWFUNCTION(RWSTRING("sfstring_Create"));

    sfs =
        (sfstring *) FieldBlock_GetElement(currentBlockContext->
                                           stringBlock);
    if (!sfs)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfstring)));
        RWRETURN((char **)NULL);
    }

    *sfs = (char *)NULL;

    RWRETURN(sfs);
}

RwBool
sfstring_Destroy(sfstring * sfs)
{
    RWFUNCTION(RWSTRING("sfstring_Destroy"));
    RWASSERT(sfs);

    if (sfs)
    {
        if (*sfs)
        {
            RwFree(*sfs);
        }

        FieldBlock_ReleaseElement(currentBlockContext->stringBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfstring_Print(sfstring * sfs)
{
    RWFUNCTION(RWSTRING("sfstring_Print"));
    RWASSERT(sfs);

    if (sfs)
    {
        LOGMSG(("%s", *sfs));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sftime methods

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

sftime             *
sftime_Create(void)
{
    sftime             *sft;

    RWFUNCTION(RWSTRING("sftime_Create"));

    sft = (sftime *)
        FieldBlock_GetElement(currentBlockContext->timeBlock);
    if (!sft)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfstring)));
        RWRETURN((double *)NULL);
    }

    *sft = 0.0L;

    RWRETURN(sft);
}

RwBool
sftime_Destroy(sftime * sft)
{
    RWFUNCTION(RWSTRING("sftime_Destroy"));
    RWASSERT(sft);

    if (sft)
    {
        FieldBlock_ReleaseElement(currentBlockContext->timeBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sftime_Print(sftime * sft)
{
    RWFUNCTION(RWSTRING("sftime_Print"));
    RWASSERT(sft);

    if (sft)
    {
        LOGMSG(("%e", *sft));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sfvec2f methods

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

sfvec2f            *
sfvec2f_Create(void)
{
    sfvec2f            *sfv2;

    RWFUNCTION(RWSTRING("sfvec2f_Create"));

    sfv2 =
        (sfvec2f *) FieldBlock_GetElement(currentBlockContext->
                                          vec2fBlock);
    if (!sfv2)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfvec2f)));
        RWRETURN((sfvec2f *)NULL);
    }

    sfv2->x = 0.0f;
    sfv2->y = 0.0f;

    RWRETURN(sfv2);
}

RwBool
sfvec2f_Destroy(sfvec2f * sfv2)
{
    RWFUNCTION(RWSTRING("sfvec2f_Destroy"));
    RWASSERT(sfv2);

    if (sfv2)
    {
        FieldBlock_ReleaseElement(currentBlockContext->vec2fBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfvec2f_Print(sfvec2f * sfv2)
{
    RWFUNCTION(RWSTRING("sfvec2f_Print"));
    RWASSERT(sfv2);

    if (sfv2)
    {
        LOGMSG((" %f %f,", sfv2->x, sfv2->y));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

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

   sfvec3f methods

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

sfvec3f            *
sfvec3f_Create(void)
{
    sfvec3f            *sfv3;

    RWFUNCTION(RWSTRING("sfvec3f_Create"));

    sfv3 =
        (sfvec3f *) FieldBlock_GetElement(currentBlockContext->
                                          vec3fBlock);
    if (!sfv3)
    {
        RWERROR((E_RW_NOMEM, sizeof(sfvec3f)));
        RWRETURN((sfvec3f *)NULL);
    }

    sfv3->x = 0.0f;
    sfv3->y = 0.0f;
    sfv3->z = 0.0f;

    RWRETURN(sfv3);
}

RwBool
sfvec3f_Destroy(sfvec3f * sfv3)
{
    RWFUNCTION(RWSTRING("sfvec3f_Destroy"));
    RWASSERT(sfv3);

    if (sfv3)
    {
        FieldBlock_ReleaseElement(currentBlockContext->vec3fBlock);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfvec3f_Print(sfvec3f * sfv3)
{
    RWFUNCTION(RWSTRING("sfvec3f_Print"));
    RWASSERT(sfv3);

    if (sfv3)
    {
        LOGMSG((" %f %f %f,", sfv3->x, sfv3->y, sfv3->z));

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
sfvec3f_compare(const sfvec3f * sfv3A, const sfvec3f * sfv3B)
{
    RWFUNCTION(RWSTRING("sfvec3f_compare"));
    RWASSERT(sfv3A);
    RWASSERT(sfv3B);

    if (sfv3A && sfv3B)
    {
        if ((sfv3A->x == sfv3B->x) &&
            (sfv3A->y == sfv3B->y) && (sfv3A->z == sfv3B->z))
        {
            RWRETURN(TRUE);
        }

        RWRETURN(FALSE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

sfvec3f            *
sfvec3f_Read(RwChar * text)
{
    RWFUNCTION(RWSTRING("sfvec3f_Read"));
    RWASSERT(text);

    if (text)
    {
        sfvec3f            *sfv3;

        sfv3 = sfvec3f_Create();
        if (!sfv3)
        {
            RWRETURN((sfvec3f *)NULL);
        }

        if (sscanf(text, "%f %f %f", &sfv3->x, &sfv3->y, &sfv3->z) != 3)
        {
            /* try with commas.... */
            if (sscanf(text, "%f, %f, %f", &sfv3->x, &sfv3->y, &sfv3->z)
                != 3)
            {
                RWRETURN((sfvec3f *)NULL);
            }
        }

        RWRETURN(sfv3);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((sfvec3f *)NULL);
}
