/*****************************************************************************
 * Copyright(C)SEGA 1995-1997/SYSTEM R&D DEPT                                *
 *                                                                           *
 * COMMAND :                                                                 *
 *                                                                           *
 * FILE:    NJReadNja.c                                                      *
 *                                                                           *
 * VERSION: 1.00                                                             *
 *                                                                           *
 * DATE: 1997/05/14                                                          *
 *                                                                           *
 *****************************************************************************/
#define __NJVReadNja_c
#include "NJVRead.h"

/*****************************************************************************
 *
 *      local
 *
 *****************************************************************************/
static int alloc_count=0;    /* for debug */
static int free_count=0;     /* for debug */

/*****************************************************************************
 *
 *      Read Nja
 *
 *****************************************************************************/
/*****************************************************************************
 *      Get size
 *****************************************************************************/
/*
 * NJV_GetMeshesSize
 */
int NJV_GetMeshesSize(NJV_WorkTab *t_p, FILE *fp)
{
    int done=0;
    int      n;
    do {
        fgets(t_p->buf, NJV_BUFFER_MAX, fp);
        NJV_GetWords(pwl, t_p->buf);
        t_p->current_line++;
        if (!strcmp(pwl->pwords[0], STR_START)) {
            n = 0;
        } else if (!strcmp(pwl->pwords[0], STR_END)) {
            done = 1;
        } else if (!strcmp(pwl->pwords[0], STR_Strip)) {
            n += pwl->word_n-2;
        } else {
            n += pwl->word_n;
        }
    } while(!done && !feof(fp));

#if 0
    fprintf(stderr, "Meshes %d\n", n);
#endif
    return n;
}

/*
 * NJV_GetPolyNormalSize
 */
int NJV_GetPolyNormalSize(NJV_WorkTab *t_p, FILE *fp)
{
    int done=0;
    int      n;
    do {
        fgets(t_p->buf, NJV_BUFFER_MAX, fp);
        NJV_GetWords(pwl, t_p->buf);
        t_p->current_line++;
        if (!strcmp(pwl->pwords[0], STR_START)) {
            n = 0;
        } else if (!strcmp(pwl->pwords[0], STR_END)) {
            done = 1;   
        } else {
            n++;
        }
    } while(!done && !feof(fp));

#if 0
    fprintf(stderr, "PolyNormal %d\n", n);
#endif
    return n;
}

int NJV_GetVertUvSize(NJV_WorkTab *t_p, FILE *fp)
{
    int done=0;
    int      n;
    do {
        fgets(t_p->buf, NJV_BUFFER_MAX, fp);
        NJV_GetWords(pwl, t_p->buf);
        t_p->current_line++;
        if (!strcmp(pwl->pwords[0], STR_START)) {
            n = 0;
        } else if (!strcmp(pwl->pwords[0], STR_END)) {
            done = 1;   
        } else {
            n++;
        }
    } while(!done && !feof(fp));

#if 0
    fprintf(stderr, "VertUv %d\n", n);
#endif
    return n;
}


/*****************************************************************************
 *      Dispatch rutines
 *****************************************************************************/
/*--------------*/
/* local macros */
/*--------------*/
#define _FGET     fgets(t_p->buf, NJV_BUFFER_MAX, fp) /* read a line     */
#define _GETW     NJV_GetWords(pwl, t_p->buf)         /* get args        */
#define _CHK(_n)  NJV_WORD_N_CHECK(pwl, _n)           /* check count     */
#define _CLP      t_p->current_line++                 /* inc currentline */
/*++*/

/*
 * ssn_ReadTEXTURE_START
 */
static int ssn_ReadTEXTURE_START(FILE *fp, NJV_WorkTab *t_p)
{
    int          done=0;
    NJS_TEXNAME  *tmp_p;

#ifdef NJV_DEBUG
    fprintf(stderr, "ssn_ReadTEXTURE_START\n");
#endif

    do {
       _FGET; _GETW; _CLP;
        if (!strcmp(pwl->pwords[0], STR_TEXN)) {
            tmp_p=&t_p->texname_list->data_p[t_p->texname_n++];
            _CHK(2);
            tmp_p->filename = strdup(pwl->pwords[1]); /* filename       */
			tmp_p->attr = 0;
			tmp_p->texaddr = 0;
		} else if (!strcmp(pwl->pwords[0], STR_TEXN3)) {
            tmp_p=&t_p->texname_list->data_p[t_p->texname_n++];
            _CHK(4);
            tmp_p->filename = (void *)NJV_ATOI(pwl->pwords[1]); /* filename       */
            tmp_p->attr = NJV_STRTOL(pwl->pwords[2], NULL, 16);
            tmp_p->texaddr = NJV_ATOI(pwl->pwords[3]);
        } else if (!strcmp(pwl->pwords[0], STR_END)) {
            done = 1;
        }
    } while (!done);

    return TRUE;
}

/*
 * ssn_ReadMATERIAL
 */

static int ssn_ReadMATERIAL(FILE *fp, NJV_WorkTab *t_p)
{
    int          done=0, count=0;
    char tmpname[NJV_STRING_MAX];
    NJV_WorkMaterial     *work_p;
    NJS_MATERIAL          *tmp_p;
    static int  defmatflag=FALSE;

#ifdef NJV_DEBUG
    fprintf(stderr, "NJV_ReadMATERIAL\n");
#endif

    _CHK(2); _CLP;
    strcpy(tmpname, pwl->pwords[1]);

    if (0 == t_p->material_n) {
        defmatflag = (!strncmp(tmpname, "defmat", 6) ? TRUE : FALSE);
    }

    do {
        _FGET; _GETW; _CLP;
        if (!strcmp(pwl->pwords[0], STR_MATSTART)) {
            /* set default material to last */
            if (1 == defmatflag) {
                if (0 == t_p->material_n) { /* default material */
                    work_p=&t_p->material_list[t_p->material_last];
                } else {
                    work_p=&t_p->material_list[t_p->material_n - 1];
                }
            } else {
                work_p=&t_p->material_list[t_p->material_n];
            }
            t_p->material_n++;
            tmp_p=work_p->data_p;
            if (0 == count++) {
                strcpy(work_p->name, tmpname);
            } else {
                strcpy(work_p->name, STR_NULL);
            }

            _FGET; _GETW; _CHK(5); _CLP;               /* diffuse     */
            tmp_p->diffuse.argb.a = NJV_ATOI(pwl->pwords[1]);
            tmp_p->diffuse.argb.r = NJV_ATOI(pwl->pwords[2]);
            tmp_p->diffuse.argb.g = NJV_ATOI(pwl->pwords[3]);
            tmp_p->diffuse.argb.b = NJV_ATOI(pwl->pwords[4]);

            _FGET; _GETW; _CHK(5); _CLP;               /* specular    */
            tmp_p->specular.argb.a = NJV_ATOI(pwl->pwords[1]);
            tmp_p->specular.argb.r = NJV_ATOI(pwl->pwords[2]);
            tmp_p->specular.argb.g = NJV_ATOI(pwl->pwords[3]);
            tmp_p->specular.argb.b = NJV_ATOI(pwl->pwords[4]);

            _FGET; _GETW; _CHK(2); _CLP;               /* expornent   */
            tmp_p->exponent = NJV_ATOF(pwl->pwords[1]);

            _FGET; _GETW; _CHK(3); _CLP;               /* AttrTexId   */
            tmp_p->attr_texId =
                NJV_STRTOL(pwl->pwords[1], NULL, 16)|NJV_ATOI(pwl->pwords[2]);

            _FGET; _GETW; _CHK(2); _CLP;               /* attrflags   */
            tmp_p->attrflags = NJV_STRTOL(pwl->pwords[1], NULL, 16);
#if 1
#define     BLEND_MASK         0xfc000000
#define     BLEND_DEFAULT_BITS 0x94000000
            if (0 == (tmp_p->attrflags & BLEND_MASK)) {
                tmp_p->attrflags |= BLEND_DEFAULT_BITS;
            }
#endif

        } else if (!strcmp(pwl->pwords[0], STR_END)) {
            done = 1;
        }
    } while (!done);

    return TRUE;
}

/*
 * ssn_ReadPOLYGON
 */
static int ssn_ReadPOLYGON(FILE *fp, NJV_WorkTab *t_p)
{
    int    i, flag, len;
    int    done = 0;
    char   *name_p = t_p->mesh_list[t_p->mesh_n].name;
    Sint16 *tmp_p = t_p->mesh_list[t_p->mesh_n++].data_p;

#ifdef NJV_DEBUG
    fprintf(stderr, "ssn_ReadPOLYGON\n");
#endif
    _CHK(2); _CLP;
    strcpy(name_p, pwl->pwords[1]);
    do {
        _FGET; _GETW; _CLP;
        if (!strcmp(pwl->pwords[0], STR_START)) {
            do {
                _FGET; _GETW; _CLP;
                if (!strcmp(pwl->pwords[0], STR_END)) {
                    done = 1;
                } else if (!strcmp(pwl->pwords[0], STR_Strip)) {
                    flag = NJV_STRTOL(pwl->pwords[1], NULL, 16);
                    len = NJV_ATOI(pwl->pwords[2]);
                    *tmp_p++ = flag | len;
                    for (i=3; i<pwl->word_n; i++) {
                        *tmp_p++ = NJV_ATOI(pwl->pwords[i]);
                    }
                } else {
                    for (i=0; i<pwl->word_n; i++) {
                        *tmp_p++ = NJV_ATOI(pwl->pwords[i]);
                    }
                }
            } while (!done);
        }
    } while (!done);

    return TRUE;
}

/*
 * ssn_ReadPOLYNORMAL
 */
static int ssn_ReadPOLYNORMAL(FILE *fp, NJV_WorkTab *t_p)
{
    int        done = 0;
    char       *name_p = t_p->polynormal_list[t_p->polynormal_n].name;
    NJS_VECTOR *tmp_p = t_p->polynormal_list[t_p->polynormal_n++].data_p;

#ifdef NJV_DEBUG
    fprintf(stderr, "ssn_ReadPOLYNORMAL\n");
#endif

    _CHK(2); _CLP;
    strcpy(name_p, pwl->pwords[1]);
    do {
        _FGET; _GETW; _CLP;
        if (!strcmp(pwl->pwords[0], STR_START)) {
            do {
                _FGET; _GETW;
                if (!strcmp(pwl->pwords[0], STR_END)) {
                    _CLP;
                    done = 1;
                } else {
                    _CHK(4); _CLP;
                    tmp_p->x = NJV_ATOF(pwl->pwords[1]);
                    tmp_p->y = NJV_ATOF(pwl->pwords[2]);
                    tmp_p->z = NJV_ATOF(pwl->pwords[3]);
                    tmp_p++;
                }
            } while (!done);
        }
    } while (!done);

    return TRUE;
}

/*
 * ssn_ReadVERTUV
 */
static int ssn_ReadVERTUV(FILE *fp, NJV_WorkTab *t_p)
{
    int       done = 0;
    char      *name_p = t_p->vertuv_list[t_p->vertuv_n].name;
    NJS_COLOR *tmp_p = t_p->vertuv_list[t_p->vertuv_n++].data_p;

#ifdef NJV_DEBUG
    fprintf(stderr, "ssn_ReadVERTUV\n");
#endif

    _CHK(2); _CLP;
    strcpy(name_p, pwl->pwords[1]);
    do {
        _FGET; _GETW; _CLP;
        if (!strcmp(pwl->pwords[0], STR_START)) {
            do {
                _FGET; _GETW;
                if (!strcmp(pwl->pwords[0], STR_END)) {
                    _CLP;
                    done = 1;
                } else {
                    _CHK(3); _CLP;
                    tmp_p->tex.u = NJV_ATOI(pwl->pwords[1]);
                    tmp_p->tex.v = NJV_ATOI(pwl->pwords[2]);
                    tmp_p++;
                }
            } while (!done);
        }
    } while (!done);

    return TRUE;
}

/*
 * ssn_ReadMESHSET
 */
static int ssn_ReadMESHSET(FILE *fp, NJV_WorkTab *t_p)
{
    int          done=0, count=0;
    NJV_WorkMeshset      *work_p;
    NJS_MESHSET           *tmp_p;
    char tmpname[NJV_STRING_MAX];

#ifdef NJV_DEBUG
    fprintf(stderr, "ssn_ReadMESHSET\n");
#endif

    _CHK(2); _CLP;
    strcpy(tmpname, pwl->pwords[1]);
    do {
        _FGET; _GETW; _CLP;
        if (!strcmp(pwl->pwords[0], STR_MESHSTART)) {
            work_p = &t_p->meshset_list[t_p->meshset_n++];
            tmp_p  = work_p->data_p;
            if (0 == count++) {
                strcpy(work_p->name, tmpname);
            } else {
                strcpy(work_p->name, STR_NULL);
            }
            _FGET; _GETW; _CHK(3); _CLP;            /* type_matId */
            tmp_p->type_matId = (unsigned short)
                NJV_STRTOL(pwl->pwords[1], NULL, 16)|NJV_ATOI(pwl->pwords[2]);

            _FGET; _GETW; _CHK(2); _CLP;            /* nbMesh     */
            tmp_p->nbMesh = NJV_ATOI(pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* meshes     */
            strcpy(work_p->meshesname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* attrs      */
            strcpy(work_p->attrsname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* normals    */
            strcpy(work_p->normalsname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* vertcolor  */
            strcpy(work_p->vertcolorname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* vertuv     */
            strcpy(work_p->vertuvname, pwl->pwords[1]);

        } else if (!strcmp(pwl->pwords[0], STR_END)) {
            done = 1;
        }
    } while (!done);

    return TRUE;
}

/*
 * ssn_ReadMODEL
 */
static int ssn_ReadMODEL(FILE *fp, NJV_WorkTab *t_p)
{
    int           done=0;
    NJV_WorkModel *work_p = &t_p->model_list[t_p->model_n++];
    NJS_MODEL     *tmp_p = work_p->data_p;

#ifdef NJV_DEBUG
    fprintf(stderr, "ssn_ReadMODEL\n");
#endif

    _CHK(2); _CLP;
    strcpy(work_p->name, pwl->pwords[1]);
    
    do {
        _FGET; _GETW; _CLP;
        if (!strcmp(pwl->pwords[0], STR_START)) {
            _FGET; _GETW; _CHK(2); _CLP;            /* points      */
            strcpy(work_p->pointsname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* normals     */
            strcpy(work_p->normalsname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* nbPoint     */
            tmp_p->nbPoint = NJV_ATOI(pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* meshsets    */
            strcpy(work_p->meshsetname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* mats        */
            strcpy(work_p->matsname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* nbMeshset   */
            tmp_p->nbMeshset = NJV_ATOI(pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* nbMat       */
            tmp_p->nbMat = NJV_ATOI(pwl->pwords[1]);

            _FGET; _GETW; _CHK(4); _CLP;            /* center      */
            tmp_p->center.x = NJV_ATOF(pwl->pwords[1]);
            tmp_p->center.y = NJV_ATOF(pwl->pwords[2]);
            tmp_p->center.z = NJV_ATOF(pwl->pwords[3]);

            _FGET; _GETW; _CHK(2); _CLP;            /* center      */
            tmp_p->r = NJV_ATOF(pwl->pwords[1]);
        } else if (!strcmp(pwl->pwords[0], STR_END)) {
            done = 1;
        }
    } while (!done);

    return TRUE;
}

/*
 * ssn_ReadOBJECT
 */
static int ssn_ReadOBJECT(FILE *fp, NJV_WorkTab *t_p)
{
    int            done=0;
    NJV_WorkObject *work_p = &t_p->object_list[t_p->object_n++];
    NJS_OBJECT     *tmp_p = work_p->data_p;

#ifdef NJV_DEBUG
    fprintf(stderr, "ssn_ReadOBJECT\n");
#endif

    _CHK(2); _CLP;
    strcpy(work_p->name, pwl->pwords[1]);
    
    do {
        _FGET; _GETW; _CLP;
        if (!strcmp(pwl->pwords[0], STR_START)) {
            _FGET; _GETW; _CHK(2); _CLP;            /* evalflags   */
            tmp_p->evalflags = NJV_STRTOL(pwl->pwords[1], NULL, 16);

            _FGET; _GETW; _CHK(2); _CLP;            /* model       */
            strcpy(work_p->modelname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(4); _CLP;            /* pos         */
            tmp_p->pos[0] = NJV_ATOF(pwl->pwords[1]);
            tmp_p->pos[1] = NJV_ATOF(pwl->pwords[2]);
            tmp_p->pos[2] = NJV_ATOF(pwl->pwords[3]);

            _FGET; _GETW; _CHK(4); _CLP;            /* ang         */ 
            tmp_p->ang[0] = (long)(NJV_ATOF(pwl->pwords[1]) * val);
            tmp_p->ang[1] = (long)(NJV_ATOF(pwl->pwords[2]) * val);
            tmp_p->ang[2] = (long)(NJV_ATOF(pwl->pwords[3]) * val);

            _FGET; _GETW; _CHK(4); _CLP;            /* scl         */
            tmp_p->scl[0] = NJV_ATOF(pwl->pwords[1]);
            tmp_p->scl[1] = NJV_ATOF(pwl->pwords[2]);
            tmp_p->scl[2] = NJV_ATOF(pwl->pwords[3]);

            _FGET; _GETW; _CHK(2); _CLP;            /* child       */
            strcpy(work_p->childname, pwl->pwords[1]);

            _FGET; _GETW; _CHK(2); _CLP;            /* sibling     */
            strcpy(work_p->siblingname, pwl->pwords[1]);

        } else if (!strcmp(pwl->pwords[0], STR_END)) {
            done = 1;
        }
    } while (!done);
    
    return TRUE;
}

/*
 * Callback Table
 */
#define CMD_ENT(_name)	ssn_Read##_name, STR_##_name
static NJV_ReadCmd cmdtab[] = {
        CMD_ENT(TEXTURE_START),
        CMD_ENT(MATERIAL),
        CMD_ENT(POLYGON),
        CMD_ENT(POLYNORMAL),
        CMD_ENT(VERTUV),
        CMD_ENT(MESHSET),
        CMD_ENT(POINT),
        CMD_ENT(NORMAL),
        CMD_ENT(MODEL),
        CMD_ENT(OBJECT),
    };
static int cmdn = sizeof(cmdtab) / sizeof(NJV_ReadCmd);

/*****************************************************************************
 *      read nja (model and texture)
 *****************************************************************************/
/*
 * NJV_ReadNja
 */
int NJV_ReadNja(NJV_Nja *nja_p, char *filename)
{
    int                     i, j;
    FILE                     *fp;
    NJV_WorkTab tab, *t_p = &tab;
    NJS_MATERIAL          *mat_p;
    NJS_MESHSET       *meshset_p;
    char              *tmpname_p;   

    /*----------------*
     *     init       *
     *----------------*/
	pwl = &wl;
    NJV_GetWordsInit(pwl);
    NJV_MallocInit(t_p);

    /* work */
    t_p->texname_n       = 0;
    t_p->texname_list    = (NJV_WorkTexname *)NULL;
    t_p->material_n      = 0;
    t_p->material_list   = (NJV_WorkMaterial *)NULL;
    t_p->mesh_n          = 0;
    t_p->mesh_list       = (NJV_WorkMeshes *)NULL;
    t_p->polynormal_n    = 0;
    t_p->polynormal_list = (NJV_WorkPolyNormal *)NULL;
    t_p->vertuv_n        = 0;
    t_p->vertuv_list     = (NJV_WorkVertUv *)NULL;
    t_p->meshset_n       = 0;
    t_p->meshset_list    = (NJV_WorkMeshset *)NULL;
    t_p->point_n         = 0;
    t_p->point_list      = (NJV_WorkPoint *)NULL;
    t_p->normal_n        = 0;
    t_p->normal_list     = (NJV_WorkNormal *)NULL;
    t_p->model_n         = 0;
    t_p->model_list      = (NJV_WorkModel *)NULL;
    t_p->object_n        = 0;
    t_p->object_list     = (NJV_WorkObject *)NULL;

    t_p->texlist_p       = (NJS_TEXLIST *)NULL;
    t_p->current_line    = 0;

    /* output */
    nja_p->object_p      = (NJS_OBJECT *)NULL;
    nja_p->texlist_p     = (NJS_TEXLIST *)NULL;

    /*----------------*
     * file open      *
     *----------------*/
    if (!(fp = fopen(filename, "r"))) {
        fprintf(stderr, "Error : Can't open %s\n", filename);
        return FALSE;
    }
    t_p->filename = NJV_Strdup(t_p, filename);

    /*-----------------*
     * phase 1 : count *
     *-----------------*/
    /* count elements */
    do {
        fgets(t_p->buf, NJV_BUFFER_MAX, fp);
        NJV_GetWords(pwl, t_p->buf);
        t_p->current_line++;
		if(0 < pwl->word_n){
			if (!strcmp(pwl->pwords[0], STR_TEXN)) {
				t_p->texname_n++;
			} else if (!strcmp(pwl->pwords[0], STR_TEXN3)) {
				t_p->texname_n++;
			} else if (!strcmp(pwl->pwords[0], STR_MATSTART)) {
				t_p->material_n++;
			} else if (!strcmp(pwl->pwords[0], STR_POLYGON)) {
				t_p->mesh_n++;
			} else if (!strcmp(pwl->pwords[0], STR_POLYNORMAL)) {
				t_p->polynormal_n++;
			} else if (!strcmp(pwl->pwords[0], STR_VERTUV)) {
				t_p->vertuv_n++;
			} else if (!strcmp(pwl->pwords[0], STR_MESHSTART)) {
				t_p->meshset_n++;
			} else if (!strcmp(pwl->pwords[0], STR_POINT)) {
				t_p->point_n++;
			} else if (!strcmp(pwl->pwords[0], STR_NORMAL)) {
				t_p->normal_n++;
			} else if (!strcmp(pwl->pwords[0], STR_MODEL)) {
				t_p->model_n++;
			} else if (!strcmp(pwl->pwords[0], STR_OBJECT)) {
				t_p->object_n++;
			}
		}
    } while(!feof(fp));

#if 0
    fprintf(stderr, "texname_n %d\n", t_p->texname_n);
    fprintf(stderr, "material_n %d\n", t_p->material_n);
    fprintf(stderr, "mesh %d\n", t_p->mesh_n);
    fprintf(stderr, "polynormal %d\n", t_p->polynormal_n);
    fprintf(stderr, "vertuv %d\n", t_p->vertuv_n);
    fprintf(stderr, "meshset_n %d\n", t_p->meshset_n);
    fprintf(stderr, "point_n %d\n", t_p->point_n);
    fprintf(stderr, "normal_n %d\n", t_p->normal_n);
    fprintf(stderr, "model_n %d\n", t_p->model_n);
    fprintf(stderr, "object_n  %d\n", t_p->object_n);
#endif

    /* malloc workbuf */
    if (t_p->texname_n > 0) {
        NJV_MCHECK(t_p->texname_list, (NJV_WorkTexname *)NJV_Malloc(t_p,
            sizeof(NJV_WorkTexname), "texname_n"));
    }

    if (t_p->material_n > 0) {
        NJV_MCHECK(t_p->material_list, (NJV_WorkMaterial *)NJV_Malloc(t_p,
            sizeof(NJV_WorkMaterial)*t_p->material_n, "material_n"));
    }

    if (t_p->mesh_n > 0) {
        NJV_MCHECK(t_p->mesh_list, (NJV_WorkMeshes *)NJV_Malloc(t_p,
            sizeof(NJV_WorkMeshes)*t_p->mesh_n, "mesh_n"));
    }

    if (t_p->polynormal_n > 0) {
        NJV_MCHECK(t_p->polynormal_list, (NJV_WorkPolyNormal *)NJV_Malloc(t_p,
            sizeof(NJV_WorkPolyNormal)*t_p->polynormal_n, "polynormal_n"));
    }

    if (t_p->vertuv_n > 0) {
        NJV_MCHECK(t_p->vertuv_list, (NJV_WorkVertUv *)NJV_Malloc(t_p,
            sizeof(NJV_WorkVertUv)*t_p->vertuv_n, "vertuv_n"));
    }

    if (t_p->meshset_n > 0) {
        NJV_MCHECK(t_p->meshset_list, (NJV_WorkMeshset *)NJV_Malloc(t_p,
            sizeof(NJV_WorkMeshset)*t_p->meshset_n, "meshset_n"));
    }

    if (t_p->point_n > 0) {
        NJV_MCHECK(t_p->point_list, (NJV_WorkPoint *)NJV_Malloc(t_p,
            sizeof(NJV_WorkPoint)*t_p->point_n, "point_n"));
    }

    if (t_p->normal_n > 0) {
        NJV_MCHECK(t_p->normal_list, (NJV_WorkNormal *)NJV_Malloc(t_p,
            sizeof(NJV_WorkNormal)*t_p->normal_n, "normal_n"));
    }

    if (t_p->model_n > 0) {
        NJV_MCHECK(t_p->model_list, (NJV_WorkModel *)NJV_Malloc(t_p,
            sizeof(NJV_WorkModel)*t_p->model_n, "model_n"));
    }

    if (t_p->object_n > 0) {
        NJV_MCHECK(t_p->object_list, (NJV_WorkObject *)NJV_Malloc(t_p,
            sizeof(NJV_WorkObject)*t_p->object_n, "object_n"));
    }

    /*------------------*
     * phase 2 : malloc *
     *------------------*/
    /* rewind */
    rewind(fp);
    t_p->current_line = 0;

    /* get mesh list size, point list size, normal list size */
    t_p->mesh_n      = 0;
    t_p->polynormal_n   = 0;
    t_p->vertuv_n       = 0;
    t_p->point_n        = 0;
    t_p->normal_n       = 0;
    do {
        fgets(t_p->buf, NJV_BUFFER_MAX, fp);
        NJV_GetWords(pwl, t_p->buf);
        t_p->current_line++;
        if (!strcmp(pwl->pwords[0], STR_POLYGON)) {
            t_p->mesh_list[t_p->mesh_n].n = NJV_GetMeshesSize(t_p, fp);
            t_p->mesh_n++;
        } else if (!strcmp(pwl->pwords[0], STR_POLYNORMAL)) {
            t_p->polynormal_list[t_p->polynormal_n].n = NJV_GetPolyNormalSize(t_p, fp);
            t_p->polynormal_n++;
        } else if (!strcmp(pwl->pwords[0], STR_VERTUV)) {
            t_p->vertuv_list[t_p->vertuv_n].n = NJV_GetVertUvSize(t_p, fp);
            t_p->vertuv_n++;
        } else if (!strcmp(pwl->pwords[0], STR_NORMAL)) {
            t_p->normal_list[t_p->normal_n].n = NJV_GetNormalSize(t_p, fp);
            t_p->normal_n++;
        } else if (!strcmp(pwl->pwords[0], STR_POINT)) {
            t_p->point_list[t_p->point_n].n = NJV_GetPointSize(t_p, fp);
            t_p->point_n++;
        }
    } while(!feof(fp));

    /* malloc output data */

    /* texname list
     * -- malloc count : 2 (texlist, texname_list->data_p)
     */ /* XXX */
    if (0 < t_p->texname_n) {
        /* texname list */
        NJV_MCHECK2(t_p->texname_list->data_p,
          NJS_TEXNAME *, sizeof(NJS_TEXNAME)*t_p->texname_n, "texname");
        /* texlist */
        NJV_MCHECK2(t_p->texlist_p,
          NJS_TEXLIST *, sizeof(NJS_TEXLIST), "texlist");
        t_p->texlist_p->textures = t_p->texname_list->data_p;
        t_p->texlist_p->nbTexture = t_p->texname_n;
    } else {
        t_p->texlist_p = NULL;
    }

    /* material list
     * -- malloc count : 1 (mat_p)
     */ /* XXX */
    NJV_MCHECK2(mat_p, NJS_MATERIAL *,
        sizeof(NJS_MATERIAL) * t_p->material_n, "material");
    for (i=0; i<t_p->material_n; i++) {
        t_p->material_list[i].data_p = &mat_p[i];
    }

    /* mesh list
     * -- malloc count : mesh_n
     */
    for (i=0; i<t_p->mesh_n; i++) {
        NJV_MCHECK2(t_p->mesh_list[i].data_p,
          Sint16 *, sizeof(Sint16) * t_p->mesh_list[i].n, "meshes");
    }

    /* polynormal list
     * -- malloc count : polynormal_n
     */
    for (i=0; i<t_p->polynormal_n; i++) {
        NJV_MCHECK2(t_p->polynormal_list[i].data_p, NJS_VECTOR *,
        sizeof(NJS_VECTOR) * t_p->polynormal_list[i].n, "polynormal");
    }

    /* vertuv list
     * -- malloc count : vertuv_n
     */
    for (i=0; i<t_p->vertuv_n; i++) {
        NJV_MCHECK2(t_p->vertuv_list[i].data_p, NJS_COLOR *,
        sizeof(NJS_COLOR) * t_p->vertuv_list[i].n, "vertuv");
    }

    /* meshset list
     * -- malloc count : 1 (meshset_p)
     */ /* XXX */
    NJV_MCHECK2(meshset_p,
        NJS_MESHSET *, sizeof(NJS_MESHSET)*t_p->meshset_n, "meshset");
    for (i=0; i<t_p->meshset_n; i++) {
        t_p->meshset_list[i].data_p = &meshset_p[i];
    }

    /* points list
     * -- malloc count : point_n
     */
    for (i=0; i<t_p->point_n; i++) {
        NJV_MCHECK2(t_p->point_list[i].data_p,
          NJS_POINT3 *, sizeof(NJS_POINT3) * t_p->point_list[i].n, "points");
    }

    /* normals list
     * -- malloc count : normal_n
     */
    for (i=0; i<t_p->normal_n; i++) {
        NJV_MCHECK2(t_p->normal_list[i].data_p,
          NJS_VECTOR *, sizeof(NJS_VECTOR) * t_p->normal_list[i].n, "normal");
    }

    /* model list
     * -- malloc count : model_n
     */
    for (i=0; i<t_p->model_n; i++) {
        NJV_MCHECK2(t_p->model_list[i].data_p,
          NJS_MODEL *, sizeof(NJS_MODEL), "model");
    }

    /* oblect list
     * -- malloc count : object_n
     */
    for (i=0; i<t_p->object_n; i++) {
        NJV_MCHECK2(t_p->object_list[i].data_p,
          NJS_OBJECT *, sizeof(NJS_OBJECT), "object");
          t_p->object_list[i].data_p->model = (NJS_MODEL *)NULL;
    }

    /*----------------*
     * phase 3 : read *
     *----------------*/
    /* rewind */
    rewind(fp);
    t_p->current_line=0;

    /* reset counter */
    t_p->texname_n      = 0;
    t_p->material_last  = t_p->material_n - 1; /* default material */
    t_p->material_n     = 0;
    t_p->mesh_n         = 0;
    t_p->polynormal_n   = 0;
    t_p->vertuv_n       = 0;
    t_p->meshset_n      = 0;
    t_p->point_n        = 0;
    t_p->normal_n       = 0;
    t_p->model_n        = 0;
    t_p->object_n       = 0;
     
    do {
        fgets(t_p->buf, NJV_BUFFER_MAX, fp);
        NJV_GetWords(pwl, t_p->buf);
        t_p->current_line++;

        for (i=0; i<cmdn; i++) {
            if (!strcmp(pwl->pwords[0], cmdtab[i].cmdstrings)) {
                if (FALSE != cmdtab[i].func(fp, t_p)) {
                    continue;
                } else {
                    fclose(fp);
                    return FALSE;
                }
            }
        }
    } while (!feof(fp));
    fclose(fp);

    /*--------------------------------------------*
     * phase 4 : chain pointers from name strings *
     *--------------------------------------------*/
    /* chain objects */
    for (i=t_p->object_n-1; i>=0; i--) {
#ifdef CHAIN_DEBUG1
        fprintf(stderr, "object %s\n", t_p->object_list[i].name);
#endif /* CHAIN_DEBUG1 */
        /* get child */
        if (!strcmp(t_p->object_list[i].childname, STR_NULL)) {
            t_p->object_list[i].data_p->child = (NJS_OBJECT *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->object_list[i].childname;
            for (j=i; j>=0; j--) {
                if (!strcmp(t_p->object_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->object_list[i].data_p->child = 
                        t_p->object_list[j].data_p;
#ifdef CHAIN_DEBUG1
                    fprintf(stderr, "    child %s\n",
                        t_p->object_list[j].name);
#endif /* CHAIN_DEBUG1 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain child %s\n",
                    tmpname_p);
            }
        }

        /* get sibling */
        if (!strcmp(t_p->object_list[i].siblingname, STR_NULL)) {
            t_p->object_list[i].data_p->sibling = (NJS_OBJECT *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->object_list[i].siblingname;
            for (j=i; j>=0; j--) {
                if (!strcmp(t_p->object_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->object_list[i].data_p->sibling = 
                        t_p->object_list[j].data_p;
#ifdef CHAIN_DEBUG1
                    fprintf(stderr, "    sibling %s\n",
                        t_p->object_list[j].name);
#endif /* CHAIN_DEBUG1 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain sibling %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain models to objects */
    for (i=t_p->object_n-1; i>=0; i--) {
#ifdef CHAIN_DEBUG2
        fprintf(stderr, "object %s\n", t_p->object_list[i].name);
#endif /* CHAIN_DEBUG2 */
        if (!strcmp(t_p->object_list[i].modelname, STR_NULL)) {
            t_p->object_list[i].data_p->model = (NJS_MODEL *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->object_list[i].modelname;
            for (j=t_p->model_n-1; j>=0; j--) {
                if (!strcmp(t_p->model_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->object_list[i].data_p->model = 
                        t_p->model_list[j].data_p;
#ifdef CHAIN_DEBUG2
        fprintf(stderr, "    model %s\n", t_p->model_list[j].name);
#endif /* CHAIN_DEBUG2 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain model %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain materiallists to models */
    for (i=t_p->model_n-1; i>=0; i--) {
#ifdef CHAIN_DEBUG3
        fprintf(stderr, "model %s\n", t_p->model_list[i].name);
#endif /* CHAIN_DEBUG3 */
        if (!strcmp(t_p->model_list[i].matsname, STR_NULL)) {
            t_p->model_list[i].data_p->mats = (NJS_MATERIAL *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->model_list[i].matsname;
            for (j=t_p->material_n-1; j>=0; j--) {
                if (!strcmp(t_p->material_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->model_list[i].data_p->mats = 
                        t_p->material_list[j].data_p;
#ifdef CHAIN_DEBUG3
        fprintf(stderr, "    material %s\n", t_p->material_list[j].name);
#endif /* CHAIN_DEBUG3 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain material %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain meshset to models */
    for (i=t_p->model_n-1; i>=0; i--) {
#ifdef CHAIN_DEBUG4
        fprintf(stderr, "model %s\n", t_p->model_list[i].name);
#endif /* CHAIN_DEBUG4 */
        if (!strcmp(t_p->model_list[i].meshsetname, STR_NULL)) {
            t_p->model_list[i].data_p->meshsets = (NJS_MESHSET *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->model_list[i].meshsetname;
            for (j=t_p->meshset_n-1; j>=0; j--) {
                if (!strcmp(t_p->meshset_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->model_list[i].data_p->meshsets = 
                        t_p->meshset_list[j].data_p;
#ifdef CHAIN_DEBUG4
        fprintf(stderr, "    meshset %s\n", t_p->meshset_list[j].name);
#endif /* CHAIN_DEBUG4 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain meshset %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain normal to models */
    for (i=t_p->model_n-1; i>=0; i--) {
#ifdef CHAIN_DEBUG5
        fprintf(stderr, "model %s\n", t_p->model_list[i].name);
#endif /* CHAIN_DEBUG5 */
        if (!strcmp(t_p->model_list[i].normalsname, STR_NULL)) {
            t_p->model_list[i].data_p->normals = (NJS_VECTOR *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->model_list[i].normalsname;
            for (j=t_p->normal_n-1; j>=0; j--) {
                if (!strcmp(t_p->normal_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->model_list[i].data_p->normals = 
                        t_p->normal_list[j].data_p;
#ifdef CHAIN_DEBUG5
        fprintf(stderr, "    normal %s\n", t_p->normal_list[j].name);
#endif /* CHAIN_DEBUG5 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain normal %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain points to models */
    for (i=t_p->model_n-1; i>=0; i--) {
#ifdef CHAIN_DEBUG6
        fprintf(stderr, "model %s\n", t_p->model_list[i].name);
#endif /* CHAIN_DEBUG6 */
        if (!strcmp(t_p->model_list[i].pointsname, STR_NULL)) {
            t_p->model_list[i].data_p->points = (NJS_POINT3 *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->model_list[i].pointsname;
            for (j=t_p->point_n-1; j>=0; j--) {
                if (!strcmp(t_p->point_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->model_list[i].data_p->points = 
                        t_p->point_list[j].data_p;
#ifdef CHAIN_DEBUG6
        fprintf(stderr, "    point %s\n", t_p->point_list[j].name);
#endif /* CHAIN_DEBUG6 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain point %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain vertuv to meshset */
    for (i=0; i<t_p->meshset_n; i++) {
#ifdef CHAIN_DEBUG7
        fprintf(stderr, "meshset %s\n", t_p->meshset_list[i].name);
#endif /* CHAIN_DEBUG7 */
        if (!strcmp(t_p->meshset_list[i].vertuvname, STR_NULL)) {
            t_p->meshset_list[i].data_p->vertuv = (NJS_COLOR *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->meshset_list[i].vertuvname;
            for (j=0; j<t_p->vertuv_n; j++) {
                if (!strcmp(t_p->vertuv_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->meshset_list[i].data_p->vertuv = 
                        t_p->vertuv_list[j].data_p;
#ifdef CHAIN_DEBUG7
        fprintf(stderr, "    vertuv %s\n", t_p->vertuv_list[j].name);
#endif /* CHAIN_DEBUG7 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain vertuv %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain normal to meshset */
    for (i=0; i<t_p->meshset_n; i++) {
#ifdef CHAIN_DEBUG8
        fprintf(stderr, "meshset %s\n", t_p->meshset_list[i].name);
#endif /* CHAIN_DEBUG8 */
        if (!strcmp(t_p->meshset_list[i].normalsname, STR_NULL)) {
            t_p->meshset_list[i].data_p->normals = (NJS_VECTOR *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->meshset_list[i].normalsname;
            for (j=0; j<t_p->normal_n; j++) {
                if (!strcmp(t_p->normal_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->meshset_list[i].data_p->normals = 
                        t_p->normal_list[j].data_p;
#ifdef CHAIN_DEBUG8
        fprintf(stderr, "    normal %s\n", t_p->normal_list[j].name);
#endif /* CHAIN_DEBUG8 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain normals %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain meshes to meshset */
    for (i=0; i<t_p->meshset_n; i++) {
#ifdef CHAIN_DEBUG9
        fprintf(stderr, "meshset %s\n", t_p->meshset_list[i].name);
#endif /* CHAIN_DEBUG9 */
        if (!strcmp(t_p->meshset_list[i].meshesname, STR_NULL)) {
            t_p->meshset_list[i].data_p->meshes = (Sint16 *)NULL;
            tmpname_p = (char *)NULL;
        } else {
            tmpname_p = t_p->meshset_list[i].meshesname;
            for (j=0; j<t_p->mesh_n; j++) {
                if (!strcmp(t_p->mesh_list[j].name, tmpname_p)) {
                    tmpname_p = (char *)NULL; 
                    t_p->meshset_list[i].data_p->meshes = 
                        t_p->mesh_list[j].data_p;
#ifdef CHAIN_DEBUG9
        fprintf(stderr, "    meshes %s\n", t_p->mesh_list[j].name);
#endif /* CHAIN_DEBUG9 */
                    break;
                }
            }
            if (NULL != tmpname_p) {
                fprintf(stderr ,"Warning : Can't chain meshes %s\n",
                    tmpname_p);
            }
        }
    }

    /* chain vertcolor to meshset */
          /* not support now */
    for (i=0; i<t_p->meshset_n; i++) {
        t_p->meshset_list[i].data_p->vertcolor = (NJS_COLOR *)NULL;
    }

    /*-------------------------------------*
     * return object tree and texlist data *
     *-------------------------------------*/
    if (NULL != t_p->texlist_p) {
        nja_p->texlist_p = t_p->texlist_p;
    }
    if (0 < t_p->object_n) {
        nja_p->object_p  = t_p->object_list[t_p->object_n-1].data_p;
    }

#if 0
    for (i=0; i<t_p->texname_n; i++) {
        ssn_PrintTexname(&t_p->texname_list->data_p[i]);
    }
  for (i=t_p->object_n-1; i>=0; i--) {
        fprintf(stderr, "%s\n", t_p->object_list[i].name);
        ssn_PrintObject(t_p->object_list[i].data_p);
    }
#endif

    /*--------------*
     * free workbuf *
     *--------------*/
    NJV_FreeAll(t_p);

    return TRUE;
}

/*****************************************************************************
 *
 *      Free
 *
 *****************************************************************************/
typedef struct _freeTab {
    NJS_MATERIAL    *mat_top_p;
    NJS_MESHSET *meshset_top_p;
} NJV_FreeTab;

/*
 * NJV_ObjectFree
 */
static void NJV_ObjectFree(NJS_OBJECT *object_p, NJV_FreeTab *ft_p)
{
    int i;
    if (NULL != object_p) {
        if (NULL != object_p->model) {
            NJS_MODEL *model_p=object_p->model;
            /* free points */
            if (NULL != model_p->points) {
                NJV_FREE(model_p->points);
            }
            /* free normals */
            if (NULL != model_p->normals) {
                NJV_FREE(model_p->normals);
            }
            /* free meshsets */
            if (NULL != model_p->meshsets) {
                /* search pointer top */
                if ((NULL == ft_p->meshset_top_p)
                    || ft_p->meshset_top_p > model_p->meshsets) {
                    ft_p->meshset_top_p = model_p->meshsets;
                }
                for (i=0; i<model_p->nbMeshset; i++) {
                    /* free meshes */
                    if (NULL != model_p->meshsets[i].meshes) {
                        NJV_FREE(model_p->meshsets[i].meshes);
                    }
                    /* free normals */
                    if (NULL != model_p->meshsets[i].normals) {
                        NJV_FREE(model_p->meshsets[i].normals);
                    }
                    /* free vertcolor */
                    if (NULL != model_p->meshsets[i].vertcolor) {
                        NJV_FREE(model_p->meshsets[i].vertcolor);
                    }
                    /* free vertuv */
                    if (NULL != model_p->meshsets[i].vertuv) {
                        NJV_FREE(model_p->meshsets[i].vertuv);
                    }
                }
            }
            /* free material */
            if (NULL != model_p->mats) {
                if ((NULL == ft_p->mat_top_p)
                    || ft_p->mat_top_p > model_p->mats) {
                    ft_p->mat_top_p = model_p->mats;
                }
            }
            NJV_FREE(model_p);
        }
        NJV_FREE(object_p);
    }
}

/*
 * ssn_ObjectFreeTrace
 */
static void ssn_ObjectFreeTrace(NJS_OBJECT *object_p, NJV_FreeTab *ft_p)
{
    NJS_OBJECT *current_p;

    if (NULL == object_p) {
        return;
    }
    current_p = object_p;

#if 0
    ssn_PrintObject(current_p);
#endif

    if (object_p->child) {
        ssn_ObjectFreeTrace(object_p->child, ft_p);
    }
    if (object_p->sibling) {
        ssn_ObjectFreeTrace(object_p->sibling, ft_p);
    }
    NJV_ObjectFree(current_p, ft_p);
}

/*
 * NJV_ReadNjaFree
 */
void NJV_ReadNjaFree(NJV_Nja *nja_p)
{
    NJV_FreeTab ftab;
    unsigned int   i;

    /* free texlist */
    if (NULL != nja_p) {
        if (NULL != nja_p->texlist_p) {
            if (NULL != nja_p->texlist_p->textures) {
                for (i=0; i<nja_p->texlist_p->nbTexture; i++) {
                    NJV_FREE(nja_p->texlist_p->textures[i].filename);
                }
                NJV_FREE(nja_p->texlist_p->textures);
            }
            NJV_FREE(nja_p->texlist_p);
        }
        /* free object tree */
        if (NULL != nja_p->object_p) {
            ftab.mat_top_p = (NJS_MATERIAL *)NULL;
            ftab.meshset_top_p = (NJS_MESHSET *)NULL;
            ssn_ObjectFreeTrace(nja_p->object_p, &ftab);
            if (NULL != ftab.mat_top_p) {
                NJV_FREE(ftab.mat_top_p);
            }
            if (NULL != ftab.meshset_top_p) {
                NJV_FREE(ftab.meshset_top_p);
            }
        }
    }
#ifdef MALLOC_DEBUG
    fprintf(stderr, "alloc_count %d free_count %d\n", alloc_count, free_count);
#endif
}


/*
 * End of File
 */
