/* ReadTDDD.c - read a TDDD (Three Dimension Data Decription) file
 *            - and write a TTDDD text file with its contents
 *            - written by Glenn M. Lewis - 9/4/91
 */

static char rcs_id[] = "$Id: readtddd.c,v 1.7 1991/10/14 15:09:18 glewis Exp glewis $";

#include <stdio.h>
#include <ctype.h>
#include "ttdddlib.h"
#define MAXLINE 132

static void process_DESC();
static OBJECT *process_EXTR();
static void process_INFO();
static void process_OBJ();

/* Here are a few necessary utilities */

static void get_name(name, size, world)
register char  *name;
register int size;
WORLD *world;
{
	while (size--) *name++ = fgetc(world->inp);
	*name = '\0';
}

static BYTE get_BYTE(world)
WORLD *world;
{
	return((BYTE)fgetc(world->inp));
}

static UBYTE get_UBYTE(world)
WORLD *world;
{
	return((UBYTE)fgetc(world->inp));
}

static WORD get_WORD(world)
WORLD *world;
{
	WORD tmp = (WORD)get_UBYTE(world)<<8;
	return(tmp|get_UBYTE(world));
}

static UWORD get_UWORD(world)
WORLD *world;
{
	UWORD tmp = (UWORD)get_UBYTE(world)<<8;
	return(tmp|get_UBYTE(world));
}

static ULONG get_ULONG(world)
WORLD *world;
{
	ULONG tmp = (ULONG)get_UWORD(world)<<16;
	return(tmp|get_UWORD(world));
}

static float get_FRACT(world)	/* This sets the precision to 10^(-3) */
WORLD *world;
{
	register float whole, fract;

	whole =  (float)get_WORD(world);
	fract = ((float)get_UWORD(world))/65536.0;
	return(whole+fract);
}

static void stuff_XYZ(st, world)
XYZ_st *st;
WORLD *world;
{
	st->val[0] = get_FRACT(world);
	st->val[1] = get_FRACT(world);
	st->val[2] = get_FRACT(world);
}

static void stuff_RGB(st, world)
RGB_st *st;
WORLD *world;
{
	st->val[0] = get_UBYTE(world);
	st->val[1] = get_UBYTE(world);
	st->val[2] = get_UBYTE(world);
}

/********************/
/* The MAIN section */
/********************/

int already_read_header = 0;
unsigned char header_storage[13];

WORLD *read_World(file)
FILE *file;
{
	int i;
	for (i=0; i<4; i++) header_storage[i] = fgetc(file);
	already_read_header = 4;
	if (strncmp(header_storage, "FORM", 4)==0) {
		while (i<12) header_storage[i++] = fgetc(file);
		already_read_header = 12;
		if (strncmp(&header_storage[8], "TDDD", 4)==0) return(read_TDDD(file));
		/* BAD IFF */
		header_storage[12] = 0;
		fprintf(stderr, "ERROR! Unknown FORM: '%s'\n", &header_storage[8]);
		return(0);
	} else {
		return(read_TTDDD(file));
	}
}

WORLD *read_TDDD(file)
FILE *file;
{
	register ULONG i, len, size;
	WORLD *world;
	char name[5];

	if (!file) return(0L);	/* File not open */

	if (!(world = (WORLD*)malloc(sizeof(WORLD)))) { OUT_MEM("WORLD"); }
	bzero((char*)world, sizeof(WORLD));
	world->inp = file;

	if (!already_read_header) {
		/* Parse the IFF TDDD file */
		get_name(name, 4, world);
		if (strcmp(name, "FORM") != 0) {
			fprintf(stderr, "ERROR: Input is not an IFF file.\n"); exit(-1); }
		if (!(len = get_ULONG(world))) {
			fprintf(stderr, "ERROR: Bad FORM length in IFF file.\n"); exit(-1); }
		get_name(name, 4, world);
		if (strcmp(name, "TDDD") != 0) {
			fprintf(stderr, "ERROR: IFF file is not a TDDD file.\n"); exit(-1); }
	} else {
		len =	(header_storage[4]<<24) | (header_storage[5]<<16) |
				(header_storage[6]<< 8) | (header_storage[7]    );
		already_read_header = 0;
	}
	len -= 4;

/* Here is the main loop: */
	while (len>0) {
		get_name(name, 4, world);
		size = get_ULONG(world);
		len -= 8;
		if (strcmp(name, "INFO")==0) process_INFO(size, world);
		else
		if (strcmp(name, "OBJ ")==0) process_OBJ(size, world);
		else {
			fprintf(stderr, "Warning: Unknown sub-chunk: '%s'...skipped\n",name);
			for (i=size; i--; ) fgetc(world->inp);				/* Skip this section */
		}
		if ((size % 2) == 1) { fgetc(world->inp); len--; }	/* Skip odd byte */
		len -= size;
	}

/* All done. */
	return(world);
}

static void process_INFO(len, world)
register ULONG len;
WORLD *world;
{
	register INFO *info;
	register ULONG i, size;
	char name[5];

	if (world->info) {
		fputs("ERROR: More than one INFO chunk!\n", stderr); exit(-1); }
	if (!(info = world->info = (INFO*)malloc(sizeof(INFO)))) { OUT_MEM("INFO"); }
	bzero((char*)world->info, sizeof(INFO));

	while (len>0) {
		get_name(name, 4, world);
		size = get_ULONG(world);
		len -= 8;
		if (strcmp(name, "BRSH")==0) {
			i = get_UWORD(world);
			if (i<0 || i>7) { i=0; fputs("BRSH error.\n", stderr); }
			get_name(info->brsh[i], 80, world);
		} else
		if (strcmp(name, "STNC")==0) {
			i = get_UWORD(world);
			if (i<0 || i>7) { i=0; fputs("STNC error.\n", stderr); }
			get_name(info->stnc[i], 80, world);
		} else
		if (strcmp(name, "TXTR")==0) {
			i = get_UWORD(world);
			if (i<0 || i>7) { i=0; fputs("TXTR error.\n", stderr); }
			get_name(info->txtr[i], 80, world);
		} else
		if (strcmp(name, "OBSV")==0) {
			if (!info->obsv) info->obsv=(OBSV*)malloc(sizeof(OBSV));
			if (!info->obsv) { OUT_MEM("OBSV"); }
			stuff_XYZ(&info->obsv->came, world);
			stuff_XYZ(&info->obsv->rota, world);
			info->obsv->foca = get_FRACT(world);
		} else
		if (strcmp(name, "OTRK")==0) {
			get_name(info->otrk, 18, world);
		} else
		if (strcmp(name, "OSTR")==0) {
			if (!info->ostr) info->ostr=(STRY*)malloc(sizeof(STRY));
			if (!info->ostr) { OUT_MEM("OSTR"); }
			get_name(info->ostr->path, 18, world);
			stuff_XYZ(&info->ostr->tran, world);
			stuff_XYZ(&info->ostr->rota, world);
			stuff_XYZ(&info->ostr->scal, world);
			info->ostr->info = get_UWORD(world);
		} else
		if (strcmp(name, "FADE")==0) {
			if (!info->fade) info->fade=(FADE*)malloc(sizeof(FADE));
			if (!info->fade) { OUT_MEM("FADE"); }
			info->fade->at = get_FRACT(world);
			info->fade->by = get_FRACT(world);
			fgetc(world->inp);		/* Skip a byte */
			stuff_RGB(&info->fade->to, world);
		} else
		if (strcmp(name, "SKYC")==0) {
			if (!info->skyc) info->skyc=(SKYC*)malloc(sizeof(SKYC));
			if (!info->skyc) { OUT_MEM("SKYC"); }
			fgetc(world->inp);		/* Skip a byte */
			stuff_RGB(&info->skyc->hori, world);
			fgetc(world->inp);		/* Skip a byte */
			stuff_RGB(&info->skyc->zeni, world);
		} else
		if (strcmp(name, "AMBI")==0) {
			if (!info->ambi) info->ambi=(RGB_st*)malloc(sizeof(RGB_st));
			if (!info->ambi) { OUT_MEM("AMBI"); }
			fgetc(world->inp);		/* Skip a byte */
			stuff_RGB(info->ambi, world);
		} else
		if (strcmp(name, "GLB0")==0) {
			if (!info->glb0) info->glb0=(BYTE*)malloc(8*sizeof(BYTE));
			if (!info->glb0) { OUT_MEM("GLB0"); }
			for (i=0; i<8; i++)
				info->glb0[i] = get_UBYTE(world);
		} else {
			fprintf(stderr, "WARNING: I don't know how to process the INFO sub-sub-chunk: '%s'\n", name);
			for (i=size; i--; ) fgetc(world->inp);				/* Skip this section */
		}
		if ((size % 2) == 1) { fgetc(world->inp); len--; }	/* Skip odd byte */
		len -= size;
	}
}

static void process_OBJ(len, world)
register ULONG len;
WORLD *world;
{
	register ULONG i, size;
	register OBJECT *p;
	char name[5];
	int depth=0;

	while (len>0) {
		get_name(name, 4, world);
		size = get_ULONG(world);
		len -= 8;
		if (strcmp(name, "EXTR")==0) {
			p = process_EXTR(size, create_object(), world);
			if (world->curobj) {
				world->curobj->next = p;
				p->parent = world->curobj->parent;
			} else {
				world->object = p;
				p->parent = 0;
			}
			world->curobj = p;
		} else if (strcmp(name, "DESC")==0) {
			p = create_object();
			if (world->num_DESC > world->num_TOBJ+depth) {  /* This is a child */
				depth++;    /* Down one in the hierarchy */
				world->curobj->child = p;
				p->parent = world->curobj;
			} else {
				if (world->curobj) {
					world->curobj->next = p;
					p->parent = world->curobj->parent;
				} else {
					world->object = p;
					p->parent = 0;
				}
			}
			world->curobj = p;
			process_DESC(size, &p->desc, world);
		} else if (strcmp(name, "TOBJ")==0) {
			world->num_TOBJ++;
			if (world->num_TOBJ > world->num_DESC) {
				fprintf(stderr, "Warning: TOBJ without DESC.  Ignored.\n");
			}
			if (world->num_TOBJ+depth > world->num_DESC) {  /* Up a level */
				depth--;
				world->curobj=world->curobj->parent;
			}
		} else {
			fprintf(stderr, "WARNING: I don't know how to process the OBJ sub-sub-chunk: '%s'\n", name);
			for (i=size; i--; ) fgetc(world->inp);				/* Skip this section */
		}
		if ((size % 2) == 1) { fgetc(world->inp); len--; }	/* Skip odd byte */
		len -= size;
	}
}

static OBJECT *process_EXTR(len, obj, world)
register ULONG len;
OBJECT *obj;
WORLD *world;
{
	register ULONG i, size;
	register EXTR *extr;
	register OBJECT *p;
	char name[5];
	MTRX *mtrx;
	WORLD *new;
	FILE *newinp;

	if (!(extr = obj->extr = (EXTR*)malloc(sizeof(EXTR))))
		{ OUT_MEM("EXTR"); }
	bzero((char*)extr, sizeof(EXTR));
	mtrx = &extr->mtrx;
	/* Initialize structure */
	mtrx->tran.val[0]  = mtrx->tran.val[1]  = mtrx->tran.val[2]  = 0.0;
	mtrx->scal.val[0]  = mtrx->scal.val[1]  = mtrx->scal.val[2]  = 1.0;
	mtrx->rota1.val[1] = mtrx->rota1.val[2] = 0.0;
	mtrx->rota2.val[0] = mtrx->rota2.val[2] = 0.0;
	mtrx->rota3.val[0] = mtrx->rota3.val[1] = 0.0;
	mtrx->rota1.val[0] = mtrx->rota2.val[1] = mtrx->rota3.val[2] = 1.0;

	while (len>0) {
		get_name(name, 4, world);
		size = get_ULONG(world);
		len -= 8;
		if (strcmp(name, "MTRX")==0) {
			stuff_XYZ(&mtrx->tran, world);
			stuff_XYZ(&mtrx->scal, world);
			stuff_XYZ(&mtrx->rota1, world);
			stuff_XYZ(&mtrx->rota2, world);
			stuff_XYZ(&mtrx->rota3, world);
		} else
		if (strcmp(name, "LOAD")==0) {
			get_name(extr->filename, 80, world);
		} else {
			fprintf(stderr, "WARNING: I don't know how to process the OBJ sub-sub-chunk: '%s'\n", name);
			for (i=size; i--; ) fgetc(world->inp);				/* Skip this section */
		}
		if ((size % 2) == 1) { fgetc(world->inp); len--; }	/* Skip odd byte */
		len -= size;
	}
	/* Now, load in the external object */
	if (!(newinp=fopen(extr->filename, "r"))) {
		fprintf(stderr, "Can't load in EXTR object: '%s'... ignored.\n",
			extr->filename);
		return(obj);
	}
	new = read_World(newinp);
	fclose(newinp);
	/* scale, rotate, and translate new object hierarchy */
	for (p=new->object; p; p=p->next)
		move_extr(p, mtrx);
	/* Free up unused memory */
	free((char*)obj->extr);
	free((char*)obj);
	obj = new->object;
	free((char*)new);
	return(obj);
}

static UBYTE defclst[3], defrlst[3], deftlst[3], defspc1[3];

void malloc_arrays(i, desc)
register int i;
register DESC *desc;
{
	if (!desc->fcount) {
		desc->fcount = i;
if (!(desc->face=(UWORD*)malloc(3*desc->fcount*sizeof(UWORD))))OUT_MEM((char*)0);
if (!(desc->clst=(UBYTE*)malloc(3*desc->fcount*sizeof(UBYTE))))OUT_MEM((char*)0);
if (!(desc->rlst=(UBYTE*)malloc(3*desc->fcount*sizeof(UBYTE))))OUT_MEM((char*)0);
if (!(desc->tlst=(UBYTE*)malloc(3*desc->fcount*sizeof(UBYTE))))OUT_MEM((char*)0);
		/* Initialize arrays */
		for (i=0; i<3*desc->fcount; i+=3) {
			desc->face[i]   = desc->face[i+1] = desc->face[i+2] = 0;
			desc->clst[i]   = defclst[0];
			desc->clst[i+1] = defclst[1];
			desc->clst[i+2] = defclst[2];
			desc->rlst[i]   = defrlst[0];
			desc->rlst[i+1] = defrlst[1];
			desc->rlst[i+2] = defrlst[2];
			desc->tlst[i]   = deftlst[0];
			desc->tlst[i+1] = deftlst[1];
			desc->tlst[i+2] = deftlst[2];
		}
	} else if (i != desc->fcount) {
		fprintf(stderr, "ERROR: FACE and [C|R|T]LST 'Count' values inconsistant.\n");
		OUT_MEM((char*)0);
	}
}

static void process_DESC(len, orig, world)
register ULONG len;
DESC **orig;
WORLD *world;
{
	register ULONG size;
	register DESC *desc;
	register int i, j;
	char name[5];

	if (!(desc = *orig = (DESC*)malloc(sizeof(DESC))))
		{ OUT_MEM("DESC"); }
	bzero((char*)desc, sizeof(DESC));

	/* Set up defaults: */
	defclst[0] = defclst[1] = defclst[2] = 240; /* TS default */
	defrlst[0] = defrlst[1] = defrlst[2] = 0;
	deftlst[0] = deftlst[1] = deftlst[2] = 0;
	defspc1[0] = defspc1[1] = defspc1[2] = 0;

	world->num_DESC++;

	while (len>0) {
		get_name(name, 4, world);
		size = get_ULONG(world);
		len -= 8;
		if (strcmp(name, "NAME")==0) {
			get_name(desc->name, 18, world);
		} else
		if (strcmp(name, "SHAP")==0) {
			if (!desc->shap) {
				if (!(desc->shap=(WORD*)malloc(2*sizeof(WORD))))
					{ OUT_MEM("SHAP"); }
				desc->shap[0] = 1;  /* TS defaults */
				desc->shap[1] = 0;
			}
			desc->shap[0] = get_UWORD(world);
			desc->shap[1] = get_UWORD(world);
		} else
		if (strcmp(name, "POSI")==0) {
			if (!desc->posi) {
				if (!(desc->posi=(XYZ_st*)malloc(sizeof(XYZ_st))))
					{ OUT_MEM("POSI"); }
			}
			stuff_XYZ(desc->posi, world);
		} else
		if (strcmp(name, "AXIS")==0) {
			if (!desc->axis) {
				if (!(desc->axis=(AXIS*)malloc(sizeof(AXIS))))
					{ OUT_MEM("AXIS"); }
				bzero((char*)desc->axis, sizeof(AXIS));
				desc->axis->xaxi.val[0] = 1;
				desc->axis->yaxi.val[1] = 1;
				desc->axis->zaxi.val[2] = 1;
			}
			stuff_XYZ(&desc->axis->xaxi, world);
			stuff_XYZ(&desc->axis->yaxi, world);
			stuff_XYZ(&desc->axis->zaxi, world);
		} else
		if (strcmp(name, "SIZE")==0) {
			if (!desc->size) {
				if (!(desc->size=(XYZ_st*)malloc(sizeof(XYZ_st))))
					{ OUT_MEM("SIZE"); }
			}
			stuff_XYZ(desc->size, world);
		} else
		if (strcmp(name, "PNTS")==0) {
			i = desc->pcount = get_UWORD(world);	/* Number of points */
			if (!(desc->pnts = (XYZ_st*)malloc(desc->pcount*sizeof(XYZ_st))))
				OUT_MEM("PNTS");
			for (j=0; j<i; j++)
				{ stuff_XYZ(&desc->pnts[j], world); }
		} else
		if (strcmp(name, "EDGE")==0) {
			i = desc->ecount = get_UWORD(world);	/* Number of edges */
			if (!(desc->edge = (UWORD*)malloc(2*desc->ecount*sizeof(UWORD))))
				OUT_MEM("EDGE");
			for (j=0; j<2*i; j++)
				{ desc->edge[j] = get_UWORD(world); }
		} else
		if (strcmp(name, "FACE")==0) {
			i = get_UWORD(world);				/* Number of faces */
			malloc_arrays(i, desc);
			for (j=0; j<i; j++) {
				desc->face[3*j]   = get_UWORD(world);
				desc->face[3*j+1] = get_UWORD(world);
				desc->face[3*j+2] = get_UWORD(world);
			}
		} else
		if (strcmp(name, "COLR")==0) {
			if (!desc->colr) {
				if (!(desc->colr = (RGB_st*)malloc(sizeof(RGB_st))))
					{ OUT_MEM("COLR"); }
			}
			fgetc(world->inp);			/* Must be zero */
			stuff_RGB((RGB_st*)&defclst[0], world);
			desc->colr->val[0] = defclst[0];
			desc->colr->val[1] = defclst[1];
			desc->colr->val[2] = defclst[2];
		} else
		if (strcmp(name, "REFL")==0) {
			if (!desc->refl) {
				if (!(desc->refl = (RGB_st*)malloc(sizeof(RGB_st))))
					{ OUT_MEM("REFL"); }
			}
			fgetc(world->inp);			/* Must be zero */
			stuff_RGB((RGB_st*)&defrlst[0], world);
			desc->refl->val[0] = defrlst[0];
			desc->refl->val[1] = defrlst[1];
			desc->refl->val[2] = defrlst[2];
		} else
		if (strcmp(name, "TRAN")==0) {
			if (!desc->tran) {
				if (!(desc->tran = (RGB_st*)malloc(sizeof(RGB_st))))
					{ OUT_MEM("TRAN"); }
			}
			fgetc(world->inp);			/* Must be zero */
			stuff_RGB((RGB_st*)&deftlst[0], world);
			desc->tran->val[0] = deftlst[0];
			desc->tran->val[1] = deftlst[1];
			desc->tran->val[2] = deftlst[2];
		} else
		if (strcmp(name, "SPC1")==0) {
			if (!desc->spc1) {
				if (!(desc->spc1 = (RGB_st*)malloc(sizeof(RGB_st))))
					{ OUT_MEM("SPC1"); }
			}
			fgetc(world->inp);			/* Must be zero */
			stuff_RGB((RGB_st*)&defspc1[0], world);
			desc->spc1->val[0] = defspc1[0];
			desc->spc1->val[1] = defspc1[1];
			desc->spc1->val[2] = defspc1[2];
		} else
		if (strcmp(name, "CLST")==0) {
			i = get_UWORD(world);		/* Number of faces */
			malloc_arrays(i, desc);
			for (j=0; j<i; j++)
				{ stuff_RGB((RGB_st*)&desc->clst[3*j], world); }
		} else
		if (strcmp(name, "RLST")==0) {
			i = get_UWORD(world);		/* Number of faces */
			malloc_arrays(i, desc);
			for (j=0; j<i; j++)
				{ stuff_RGB((RGB_st*)&desc->rlst[3*j], world); }
		} else
		if (strcmp(name, "TLST")==0) {
			i = get_UWORD(world);		/* Number of faces */
			malloc_arrays(i, desc);
			for (j=0; j<i; j++)
				{ stuff_RGB((RGB_st*)&desc->tlst[3*j], world); }
		} else
		if (strcmp(name, "TPAR")==0) {
			if (!desc->tpar) {
				if (!(desc->tpar=(double*)malloc(16*sizeof(double))))
					{ OUT_MEM("TPAR"); }
				bzero((char*)desc->tpar, 16*sizeof(double));
			}
			for (i=0; i<16; i++)
				desc->tpar[i] = get_FRACT(world);
		} else
		if (strcmp(name, "SURF")==0) {
			if (!desc->surf) {
				if (!(desc->surf=(UBYTE*)malloc(5*sizeof(UBYTE))))
					{ OUT_MEM("SURF"); }
				bzero((char*)desc->surf, 5*sizeof(UBYTE));
			}
			for (i=0; i<5; i++)
				desc->surf[i] = get_BYTE(world);
		} else
		if (strcmp(name, "MTTR")==0) {
			if (!desc->mttr) {
				if (!(desc->mttr=(MTTR*)malloc(sizeof(MTTR))))
					{ OUT_MEM("MTTR"); }
				bzero((char*)desc->mttr, sizeof(MTTR));
			}
			desc->mttr->type = get_UBYTE(world);
			desc->mttr->indx = (get_UBYTE(world)/100.0)+1.0;
		} else
		if (strcmp(name, "SPEC")==0) {
			if (!desc->spec) {
				if (!(desc->spec=(UBYTE*)malloc(2*sizeof(UBYTE))))
					{ OUT_MEM("SPEC"); }
				bzero((char*)desc->spec, 2*sizeof(UBYTE));
			}
			desc->spec[0] = get_UBYTE(world);
			desc->spec[1] = get_UBYTE(world);
		} else
		if (strcmp(name, "PRP0")==0) {
			if (!desc->prp0) {
				if (!(desc->prp0=(UBYTE*)malloc(6*sizeof(UBYTE))))
					{ OUT_MEM("PRP0"); }
				bzero((char*)desc->prp0, 6*sizeof(UBYTE));
			}
			for (i=0; i<6; i++)
				desc->prp0[i] = get_UBYTE(world);
		} else
		if (strcmp(name, "PRP1")==0) {
			if (!desc->prp1) {
				if (!(desc->prp1=(UBYTE*)malloc(8*sizeof(UBYTE))))
					{ OUT_MEM("PRP1"); }
				bzero((char*)desc->prp1, 8*sizeof(UBYTE));
			}
			for (i=0; i<8; i++)
				desc->prp1[i] = get_UBYTE(world);
		} else
		if (strcmp(name, "INTS")==0) {
			if (!desc->ints) {
				if (!(desc->ints=(double*)malloc(sizeof(double))))
					{ OUT_MEM("INTS"); }
			}
			*desc->ints = get_FRACT(world);
		} else
		if (strcmp(name, "INT1")==0) {
			if (!desc->int1) desc->int1=(RGB_st*)malloc(sizeof(RGB_st));
			if (!desc->int1) { OUT_MEM("INT1"); }
			stuff_RGB(desc->int1, world);
		} else
		if (strcmp(name, "STRY")==0) {
			get_name(desc->stry->path, 18, world);
			stuff_XYZ(&desc->stry->tran, world);
			stuff_XYZ(&desc->stry->rota, world);
			stuff_XYZ(&desc->stry->scal, world);
			desc->stry->info = get_UWORD(world);
		} else {
			fprintf(stderr, "WARNING: I don't know how to process the OBJ sub-sub-chunk: '%s'\n", name);
			for (i=size; i--; ) fgetc(world->inp);				/* Skip this section */
		}
		if ((size % 2) == 1) { fgetc(world->inp); len--; }	/* Skip odd byte */
		len -= size;
	}
}

