/***********************************************************
*
*	This file is Copyright © 1990-1994 Dan Wesnor
*
*	This file and any executables resulting from compilation
*	of this file are freely distributable so long as the
*	above copyright statement is included intact.  This
*	statement of distributability is limited to this file
*	only, and does not include any other file in this
*	archive.
*
*	No gaurantees of usability are made for this file or
*	any executables generated from it.  Use at your own risk.
*
************************************************************
*
*	This program generates an MC script from a 3DDD object
*	file as generated by Impulse's renderers (Turbo Silver
*	and Imagine).  This version show how to generate objects
*	using vertex based definitions of triangles.
*
*	The 3DDD parser is not all that great, and could be
*	improved upon.  Someone could also use this code as
*	a basis for a program which generates scripts from other 
*	object formats as well.
*
*	NOTE: be careful using this code on machines with
*	int lengths other than 32 bits!
*
***********************************************************/

#include "turbo.h"
#include <fcntl.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#ifndef max
#define max(a,b)	(((a)>(b))?(a):(b))
#endif
#ifndef min
#define min(a,b)	(((a)<(b))?(a):(b))
#endif

typedef struct {
	unsigned int	form,
					size,
					type;
} Form, *FormPtr;

#define MakeName(s)	(((s)[0]<<24)+((s)[1]<<16)+((s)[2]<<8)+(s)[3])

typedef struct {
	unsigned int	name,
					len;
} Chunk, *ChunkPtr;

#define ReadData(f,b,s)	read((f),(b),(s)+((s)%2))	/* force a read of even bytes */


typedef struct str_surface {
	struct str_surface	*next;
	int					num;
	COLOR				colr,
						refl,
						tran;
	UBYTE				hard,
						spec,
						index;
	char				name[32];
} Surface, *SurfacePtr;
	

#define QUIET		0x0
#define TERSE		0x1
#define VERBOSE		0x2


#define FND_PNTS	0x1
#define FND_POSI	0x2
#define FND_NAME	0x4
#define FND_SHAP	0x8
#define FND_AXIS	0x10
#define FND_SIZE	0x20
#define FND_EDGE	0x40
#define FND_FACE	0x80
#define FND_COLR	0x100
#define FND_REFL	0x200
#define FND_TRAN	0x400
#define FND_CLST	0x800
#define FND_SPEC	0x1000
#define FND_TLST	0x2000
#define FND_RLST	0x4000
#define FND_MTTR	0x8000
#define FND_INTS	0x10000
#define FND_PRP0	0x20000
#define FND_SPC1	0x40000
#define FND_PRP1	0x80000


FILE *ofh=0;
char *filename;
unsigned char printmode = TERSE;
long flags=0;

#define FL_OBJECTS	0x1

#define CHECKTERSE 	if (printmode >= TERSE)
#define CHECKVERBOSE	if (printmode >= VERBOSE)

float scalefactor=1.0;


SurfacePtr surfList=NULL;

SurfacePtr MakeSurface(UBYTE cr, UBYTE cg, UBYTE cb, UBYTE rr, UBYTE rg, UBYTE rb, UBYTE tr, UBYTE tg, UBYTE tb, UBYTE h, UBYTE s, UBYTE i)
{
	SurfacePtr	surf;
	
	surf = surfList;
	
	while (surf) 
	{
		if ((surf->colr[0] == cr) && (surf->colr[1] == cg) && (surf->colr[2] == cb)
				&& (surf->refl[0] == rr) && (surf->refl[1] == rg) 
				&& (surf->refl[2] == rb)
				&& (surf->tran[0] == tr) && (surf->tran[1] == tg) 
				&& (surf->tran[2] == tb)
				&& (surf->hard == h) && (surf->spec == s) 
				&& (surf->index == i))
			return surf;
		surf = surf->next;
	}

	if (!(surf = (SurfacePtr)calloc(1, sizeof(Surface)))) 
	{
		fprintf(stdout, "Cannot allocate new color\n");
		return surfList;
	}
	
	surf->num = surfList ? surfList->num+1 : 0;
	surf->next = surfList;
	surfList = surf;
	surf->colr[0] = cr;
	surf->colr[1] = cg;
	surf->colr[2] = cb;
	surf->refl[0] = rr;
	surf->refl[1] = rg;
	surf->refl[2] = rb;
	surf->tran[0] = tr;
	surf->tran[1] = tg;
	surf->tran[2] = tb;
	surf->hard = h;
	surf->spec = s;
	surf->index = i;
	sprintf(surf->name, "T2AS_%s_%d", filename, surf->num);
	
	fprintf(ofh, "color %s {\n", surf->name);
	fprintf(ofh, "\tdiff\t<%f, %f, %f>\n", cr/255.0, cg/255.0, cb/255.0);
	if (tr && tg && tb)
		fprintf(ofh, "\ttrans\t<%f, %f, %f>\n", tr/255.0, tg/255.0, tb/255.0);
	if (rr && rg && rb)
		fprintf(ofh, "\trefl\t<%f, %f, %f>\n", rr/255.0, rg/255.0, rb/255.0);
	if (s) 
	{
		fprintf(ofh, "\tpcoef\t%f\n", h/5.0);
		fprintf(ofh, "\tprefl\t%f\n", s/255.0);
	}
	if (i)
		fprintf(ofh, "\tindex\t%f\n", i/100.0+1.0);
	fprintf(ofh, "}\n\n");

	return surf;
}



float imn[3]={1e6, 1e6, 1e6}, imx[3]={-1e6, -1e6, -1e6};

void GetDescChunk(int ifd, int len)
{
	Chunk		chunk;
	int			whatami=0;
	int			found=0;
	int			i, p1, p2, p3;
	int			degen;
	SurfacePtr	surf;
	PNTS	 	*pnts;
	POSI 		posi;
	NAME 		name;
	SHAP 		shap;
	AXIS 		axis;
	SIZE 		size;
	EDGE 		*edge;
	FACE 		*face;
	COLR 		colr;
	REFL 		refl;
	TRAN 		tran;
	SPC1		spc1;
	CLST 		*clst;
	SPEC 		spec;
	TLST 		*tlst;
	RLST 		*rlst;
	MTTR 		mttr;
	INTS 		ints;
	PRP0 		prp;
	PRP1 		prp1;
	
	colr.col[0] = colr.col[1] = colr.col[2] = 255;
	refl.col[0] = refl.col[1] = refl.col[2] = 0;
	tran.col[0] = tran.col[1] = tran.col[2] = 0;

	while (len) 
	{
		len -= read(ifd, &chunk, sizeof(Chunk));
		chunk.len += chunk.len % 2;

		if (chunk.name == MakeName("NAME")) 
		{
			len -= ReadData(ifd, &name, sizeof(NAME));
			CHECKVERBOSE { fprintf(stdout, "\t\tNAME:  %s\n", name.Name); }
		}

		else if (chunk.name == MakeName("SHAP")) 
		{
			len -= ReadData(ifd, &shap, sizeof(SHAP));
			CHECKVERBOSE { fprintf(stdout, "\t\tSHAP:  "); }
			switch (shap.Lamp) 
			{
				case LMP_NOTALAMP:
					whatami = shap.Shape;
					switch (shap.Shape) 
					{
						case SHP_SPHERE:
							CHECKVERBOSE { fprintf(stdout, "sphere\n"); }
							break;
				
						case SHP_STENCIL:
							CHECKVERBOSE { fprintf(stdout, "stencil\n"); }
							break;

						case SHP_AXIS:
							CHECKVERBOSE { fprintf(stdout, "axis\n"); }
							break;
					
						case SHP_FACETS:
							CHECKVERBOSE { fprintf(stdout, "facets\n"); }
							break;
				
						case SHP_SURFACE:
							CHECKVERBOSE { fprintf(stdout, "Surface\n"); }
							break;
				
						case SHP_GROUND:
							CHECKVERBOSE { fprintf(stdout, "Ground\n"); }
							break;
				
						default:
							CHECKTERSE { fprintf(stdout, "Unknown:  %d\n", 
									shap.Shape); }
							break;
					}
					break;

				case LMP_SUN:
					CHECKVERBOSE { fprintf(stdout, "Lamp (Sun)\n"); }
					break;
				
				case LMP_LAMP:
					CHECKVERBOSE { fprintf(stdout, "Lamp (Lamp)\n"); }
					break;
					
				default:
					break;
			}		
		}

		else if (chunk.name == MakeName("POSI")) 
		{
			found |= FND_POSI;
			len -= ReadData(ifd, &posi, sizeof(POSI));
			CHECKVERBOSE { fprintf(stdout, "\t\tPOSI:  <%f, %f, %f>\n", 
					posi.Position.X/65536.0, 
					posi.Position.Y/65536.0, posi.Position.Z/65536.0); }
		}

		else if (chunk.name == MakeName("AXIS")) 
		{
			found |= FND_AXIS;
			len -= ReadData(ifd, &axis, sizeof(AXIS));
			CHECKVERBOSE { fprintf(stdout, "\t\tAXIS:  X = <%f, %f, %f>\n", 
					axis.XAxis.X/65536.0, axis.XAxis.Z/65536.0, 
					axis.XAxis.Y/65536.0); }
			CHECKVERBOSE { fprintf(stdout, "\t\t       Y = <%f, %f, %f>\n", 
					axis.YAxis.X/65536.0, axis.YAxis.Z/65536.0, 
					axis.YAxis.Y/65536.0); }
			CHECKVERBOSE { fprintf(stdout, "\t\t       Z = <%f, %f, %f>\n", 
					axis.ZAxis.X/65536.0, axis.ZAxis.Z/65536.0, 
					axis.ZAxis.Y/65536.0); }
		}

		else if (chunk.name == MakeName("SIZE")) 
		{
			found |= FND_SIZE;
			len -= ReadData(ifd, &size, sizeof(SIZE));
			CHECKVERBOSE { fprintf(stdout, "\t\tSIZE:  <%f, %f, %f>\n", 
					size.Size.X/65536.0, size.Size.Y/65536.0, 
					size.Size.Z/65536.0); }
		}

		else if (chunk.name == MakeName("PNTS")) 
		{
			if (pnts = (PNTS *)calloc(1, chunk.len)) 
			{
				found |= FND_PNTS;
				len -= ReadData(ifd, &(pnts->PCount), 2);
				len -= ReadData(ifd, pnts->Points, chunk.len-2);
				CHECKVERBOSE { fprintf(stdout, "\t\tPNTS:  %d\n", pnts->PCount); }
				fprintf(ofh, "flushverts\n\n");
				for (i=0; i<pnts->PCount; i++)
					fprintf(ofh, "vertex T2AV_%d <%f, %f, %f>\n", i, -scalefactor*pnts->Points[i].X/65536.0,
							scalefactor*pnts->Points[i].Z/65536.0, scalefactor*pnts->Points[i].Y/65536.0);
				fprintf(ofh, "\n");
			}
			else
				fprintf(stdout, "PNTS found, no memory to load\n");
		}

		else if (chunk.name == MakeName("EDGE")) 
		{
			if (edge = (EDGE *)calloc(1, chunk.len)) 
			{
				found |= FND_EDGE;
				len -= ReadData(ifd, edge, chunk.len);
				CHECKVERBOSE { fprintf(stdout, "\t\tEDGE:  %d\n", edge->ECount); }
			}
			else
				fprintf(stdout, "EDGE found, no memory to load\n");
		}

		else if (chunk.name == MakeName("FACE")) 
		{
			if (face = (FACE *)calloc(1, chunk.len)) 
			{
				found |= FND_FACE;
				len -= ReadData(ifd, face, chunk.len);
				CHECKVERBOSE { fprintf(stdout, "\t\tFACE:  %d\n", face->TCount); }
			}
			else
				fprintf(stdout, "FACE found, no memory to load\n");
		}

		else if (chunk.name == MakeName("COLR")) 
		{
			found |= FND_COLR;
			len -= ReadData(ifd, &colr, sizeof(COLR));
			CHECKVERBOSE { fprintf(stdout, "\t\tCOLR:  %d %d %d\n", 
					colr.col[0], colr.col[1], colr.col[2]); }
		}

		else if (chunk.name == MakeName("REFL")) 
		{
			found |= FND_REFL;
			len -= ReadData(ifd, &refl, sizeof(REFL));
			CHECKVERBOSE { fprintf(stdout, "\t\tREFL:  %d %d %d\n", 
					refl.col[0], refl.col[1], refl.col[2]); }
		}

		else if (chunk.name == MakeName("TRAN")) 
		{
			found |= FND_TRAN;
			len -= ReadData(ifd, &tran, sizeof(TRAN));
			CHECKVERBOSE { fprintf(stdout, "\t\tTRAN:  %d %d %d\n", 
					tran.col[0], tran.col[1], tran.col[2]); }
		}

		else if (chunk.name == MakeName("SPC1"))
		{
			found |= FND_SPC1;
			len -= ReadData(ifd, &tran, sizeof(TRAN));
			CHECKVERBOSE fprintf(stdout, "\t\tSPC1:  %d %d %d\n",
					spc1.col[0], spc1.col[1], spc1.col[2]);
		}

		else if (chunk.name == MakeName("CLST")) 
		{
			if (clst = (CLST *)calloc(1, chunk.len)) 
			{
				found |= FND_CLST;
				len -= ReadData(ifd, clst, chunk.len);
				CHECKVERBOSE { fprintf(stdout, "\t\tCLST:  %d\n", clst->count); }
			}
			else
				fprintf(stdout, "CLST found, no memory to load\n");
		}

		else if (chunk.name == MakeName("RLST")) 
		{
			if (rlst = (RLST *)calloc(1, chunk.len)) 
			{
				found |= FND_RLST;
				len -= ReadData(ifd, rlst, chunk.len);
				CHECKVERBOSE { fprintf(stdout, "\t\tRLST:  %d\n", rlst->count); }
			}
			else
				fprintf(stdout, "RLST found, no memory to load\n");
		}

		else if (chunk.name == MakeName("TLST")) 
		{
			if (tlst = (TLST *)calloc(1, chunk.len)) 
			{
				found |= FND_TLST;
				len -= ReadData(ifd, tlst, chunk.len);
				CHECKVERBOSE { fprintf(stdout, "\t\tTLST:  %d\n", tlst->count); }
			}
			else
				fprintf(stdout, "TLST found, no memory to load\n");
		}

		else if (chunk.name == MakeName("TPAR")) 
		{
			CHECKVERBOSE { fprintf(stdout, "\t\tTPAR:  found\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}

		else if (chunk.name == MakeName("SURF")) 
		{
			fprintf(stdout, "\t\tSURF:  found\n");
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}

		else if (chunk.name == MakeName("MTTR")) 
		{
			found |= FND_MTTR;
			len -= ReadData(ifd, &mttr, sizeof(MTTR));
			switch (mttr.Type) 
			{
				case RFR_AIR:
					CHECKVERBOSE { fprintf(stdout, "\t\tMTTR:  %f (Air)\n", VAL_AIR); }
					break;

				case RFR_WATER:
					CHECKVERBOSE { fprintf(stdout, "\t\tMTTR:  %f (Water)\n", 
							VAL_WATER); }
					break;

				case RFR_GLASS:
					CHECKVERBOSE { fprintf(stdout, "\t\tMTTR:  %f (Glass)\n", 
							VAL_GLASS); }
					break;

				case RFR_CRYSTAL:
					CHECKVERBOSE { fprintf(stdout, "\t\tMTTR:  %f (Crystal)\n", 
							VAL_CRYSTAL); }
					break;

				case RFR_CUSTOM:
					CHECKVERBOSE { fprintf(stdout, "\t\tMTTR:  %f (Custom)\n", 
							VAL_CUSTOM(mttr.Index)); }
					break;

				default:
					CHECKTERSE { fprintf(stdout, "\t\tMTTR:  unknown type %d\n", 
							mttr.Type); }
					break;
			}
		}

		else if (chunk.name == MakeName("SPEC")) 
		{
			found |= FND_SPEC;
			len -= ReadData(ifd, &spec, sizeof(SPEC));
			fprintf(stdout, "\t\tSPEC:  Spec=%d, Hard=%d\n", 
					spec.Specularity, spec.Hardness);
		}

		else if (chunk.name == MakeName("PRP0")) 
		{
			found |= FND_PRP0;
			len -= ReadData(ifd, &prp, sizeof(PRP0));
			CHECKVERBOSE { fprintf(stdout, "\t\tPRP0:  Blend = %d\n", prp.Props[PRP_BLEND]); }
			CHECKVERBOSE { fprintf(stdout, "\t\t       Rough = %d\n", prp.Props[PRP_SMOOTH]); }
			CHECKVERBOSE { fprintf(stdout, "\t\t       Shade = %s\n", 
					prp.Props[PRP_SHADE] ? "ON" : "OFF"); }
			CHECKVERBOSE { fprintf(stdout, "\t\t       Phong = %s\n", 
					prp.Props[PRP_PHONG] ? "OFF" : "ON"); }
			CHECKVERBOSE { fprintf(stdout, "\t\t       Glossy = %s\n", 
					prp.Props[PRP_GLOSSY] ? "ON" : "OFF"); }
			CHECKVERBOSE { fprintf(stdout, "\t\t       Quick = %s\n", 
					prp.Props[PRP_QUICK] ? "ON" : "OFF"); }
		}

		else if (chunk.name = MakeName("PRP1"))
		{
			found |= FND_PRP1;
			len -= ReadData(ifd, &prp1, sizeof(PRP1));
			CHECKVERBOSE fprintf(stdout, "\t\tPRP1:  Blend = %d\n", prp1.Props[PRP1_DITHER]);
			CHECKVERBOSE fprintf(stdout, "\t\t       Hardness = %d\n", prp1.Props[PRP1_HARD]);
			CHECKVERBOSE fprintf(stdout, "\t\t       Roughness = %d\n", prp1.Props[PRP1_ROUGH]);
			CHECKVERBOSE fprintf(stdout, "\t\t       Shine = %d\n", prp1.Props[PRP1_SHINY]);
			CHECKVERBOSE fprintf(stdout, "\t\t       Index = %d\n", prp1.Props[PRP1_INDEX]);
			CHECKVERBOSE fprintf(stdout, "\t\t       Quick Draw = %s\n", (prp1.Props[PRP1_QUICK])?"ON":"OFF");
			CHECKVERBOSE fprintf(stdout, "\t\t       Phong = %s\n", (prp1.Props[PRP1_PHONG])?"ON":"OFF");
			CHECKVERBOSE fprintf(stdout, "\t\t       Genlock = %s\n", (prp1.Props[PRP1_GENLOCK])?"ON":"OFF");
		}
		else if (chunk.name == MakeName("INTS")) 
		{
			found |= FND_INTS;
			len -= ReadData(ifd, &ints, sizeof(INTS));
			CHECKVERBOSE { fprintf(stdout, "\t\tINTS:  %f\n", ints.Intensity/65536.0); }
		}

		else if (chunk.name == MakeName("STRY")) 
		{
			CHECKVERBOSE { fprintf(stdout, "\t\tSTRY:  found\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}

		else 
		{
			CHECKTERSE { fprintf(stdout, "\t\t%.4s:  Found unknown chunk, skipping\n", &chunk.name); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
	}

	if (whatami == SHP_AXIS) 
	{
		float mx[3]={-1e6, -1e6, -1e6}, mn[3]={1e6, 1e6, 1e6};

		degen = 0;
		
		if (pnts && edge && face)
			CHECKTERSE { fprintf(stdout, "Dumping axis type object named \"%s\" with %d points, %d edges, and %d faces.\n",
					name.Name, pnts->PCount, edge->ECount, face->TCount); }

		fprintf(ofh, "/* --------- %s --------- */\n", name.Name);

		if (((found & FND_PRP0) && (!(prp.Props[PRP_SMOOTH]))) || ((found & FND_PRP1) && (!(prp1.Props[PRP1_PHONG]))))
			fprintf(ofh, "smoothon\n");

		if ((found & (FND_PNTS | FND_EDGE | FND_FACE)) 
				!= (FND_PNTS | FND_EDGE | FND_FACE))
			{ CHECKTERSE fprintf(stdout, "***\tError:  One of PNTS, EDGE, or FACE not found or allocated\n"); }
		else 
		{
			for (i=0; i<pnts->PCount; i++) 
			{
				pnts->Points[i].X *= -1.0;
				mx[0] = max(mx[0], pnts->Points[i].X/65536.0);
				mn[0] = min(mn[0], pnts->Points[i].X/65536.0);
				imx[0] = max(imx[0], pnts->Points[i].X/65536.0);
				imn[0] = min(imn[0], pnts->Points[i].X/65536.0);
				mx[1] = max(mx[1], pnts->Points[i].Y/65536.0);
				mn[1] = min(mn[1], pnts->Points[i].Y/65536.0);
				imx[1] = max(imx[1], pnts->Points[i].Y/65536.0);
				imn[1] = min(imn[1], pnts->Points[i].Y/65536.0);
				mx[2] = max(mx[2], pnts->Points[i].Z/65536.0);
				mn[2] = min(mn[2], pnts->Points[i].Z/65536.0);
				imx[2] = max(imx[2], pnts->Points[i].Z/65536.0);
				imn[2] = min(imn[2], pnts->Points[i].Z/65536.0);
			}

			for (i=0; i<face->TCount; i++) 
			{
				surf = MakeSurface(
					(found & FND_CLST)? clst->colors[i][0]: colr.col[0],
					(found & FND_CLST)? clst->colors[i][1]: colr.col[1],
					(found & FND_CLST)? clst->colors[i][2]: colr.col[2],
					(found & FND_RLST)? rlst->colors[i][0]: refl.col[0],
					(found & FND_RLST)? rlst->colors[i][1]: refl.col[1],
					(found & FND_RLST)? rlst->colors[i][2]: refl.col[2],
					(found & FND_TLST)? tlst->colors[i][0]: tran.col[0],
					(found & FND_TLST)? tlst->colors[i][1]: tran.col[1],
					(found & FND_TLST)? tlst->colors[i][2]: tran.col[2],
					(found & FND_SPEC) ? spec.Hardness : 0,
					(found & FND_SPEC) ? spec.Specularity : 0,
					(found & FND_MTTR) ? mttr.Index : 0);
					
				p1 = edge->Edges[face->Connects[i][0]][0];
				p2 = edge->Edges[face->Connects[i][0]][1];
				p3 = edge->Edges[face->Connects[i][1]][0];
				p3 = ((p1 == p3) || (p2 == p3)) ? 
						edge->Edges[face->Connects[i][1]][1] : p3;

				if ((pnts->Points[p1].X == pnts->Points[p2].X) 
						&& (pnts->Points[p1].Y == pnts->Points[p2].Y) 
						&& (pnts->Points[p1].Z == pnts->Points[p2].Z))
					degen++;
				else if ((pnts->Points[p1].X == pnts->Points[p3].X) 
						&& (pnts->Points[p1].Y == pnts->Points[p3].Y) 
						&& (pnts->Points[p1].Z == pnts->Points[p3].Z))
					degen++;
				else if ((pnts->Points[p2].X == pnts->Points[p3].X) 
						&& (pnts->Points[p2].Y == pnts->Points[p3].Y) 
						&& (pnts->Points[p2].Z == pnts->Points[p3].Z))
					degen++;
				else 
				{
					fprintf(ofh, "triangle {\n");
					fprintf(ofh, "\tp1\tT2AV_%d\n", p1);
					fprintf(ofh, "\tp2\tT2AV_%d\n", p2);
					fprintf(ofh, "\tp3\tT2AV_%d\n", p3);
					fprintf(ofh, "\tpatt\t%s\n", surf->name);
					fprintf(ofh, "}\n\n");
				}
			}
			if (degen) 
				CHECKTERSE { fprintf(stdout, "%d of %d triangle degenerative\n", degen, face->TCount); }
			CHECKTERSE { fprintf(stdout, "Axis bounds: <%f, %f, %f> - <%f, %f, %f>\n\n",
					 mn[0], mn[2], mn[1], mx[0], mx[2], mx[1]); }
		}
		if (((found & FND_PRP0) && (!(prp.Props[PRP_SMOOTH]))) || ((found & FND_PRP1) && (!(prp1.Props[PRP1_PHONG]))))
			fprintf(ofh, "smoothoff\n");

	}

	if (found & FND_PNTS)
		free(pnts);
	if (found & FND_EDGE)
		free(edge);
	if (found & FND_FACE)
		free(face);
	if (found & FND_TLST)
		free(tlst);
	if (found & FND_RLST)
		free(rlst);
	if (found & FND_CLST)
		free(clst);
}






void GetObjChunk(int ifd, int len)
{
	Chunk	chunk;
	
	while (len) {
		len -= read(ifd, &chunk, sizeof(Chunk));

		if (chunk.name == MakeName("EXTR")) {
			CHECKVERBOSE { fprintf(stdout, "\tEXTR:  found\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
		
		else if (chunk.name == MakeName("TOBJ")) {
			CHECKVERBOSE { fprintf(stdout, "\tTOBJ:\n\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
		
		else if (chunk.name == MakeName("DESC")) {
			CHECKVERBOSE { fprintf(stdout, "\tDESC:\n"); }
			GetDescChunk(ifd, chunk.len);
			len -= chunk.len;
		}
		
		else {
			CHECKTERSE { fprintf(stdout, "\t%.4s:  Found unknown chunk\n", &chunk.name); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
	}
}






void GetInfoChunk(int ifd, int len)
{
	Chunk	chunk;
	
	while (len) {
		len -= read(ifd, &chunk, sizeof(Chunk));

		if (chunk.name == MakeName("BRSH")) {
			BRSH brsh;
			len -= ReadData(ifd, &brsh, sizeof(BRSH));
			CHECKVERBOSE { fprintf(stdout, "\tBRSH:  #%d = %s\n", brsh.Number, brsh.Filename); }
		}
		
		else if (chunk.name == MakeName("STNC")) {
			STNC stnc;
			len -= ReadData(ifd, &stnc, sizeof(STNC));
			CHECKVERBOSE { fprintf(stdout, "\tSTNC:  #%d = %s\n", stnc.Number, stnc.Filename); }
		}
		
		else if (chunk.name == MakeName("TXTR")) {
			TXTR txtr;
			len -= ReadData(ifd, &txtr, sizeof(TXTR));
			CHECKVERBOSE { fprintf(stdout, "\tTXTR:  #%d = %s\n", txtr.Number, txtr.Filename); }
		}
		
		else if (chunk.name == MakeName("OBSV")) {
			OBSV obsv;
			len -= ReadData(ifd, &obsv, sizeof(OBSV));
			CHECKTERSE { fprintf(stdout, "\tOBSV:  Location = <%f, %f, %f>\n", obsv.Camera.X/65536.0, obsv.Camera.Y/65536, obsv.Camera.Z/65536.0); }
			CHECKTERSE { fprintf(stdout, "\t       Rotate = <%f, %f, %f>\n", obsv.Rotate.X/65536.0, obsv.Rotate.Y/65536, obsv.Rotate.Z/65536.0); }
			CHECKTERSE { fprintf(stdout, "\t       Focal Len. = %f\n", obsv.Focal/65536.0); }
		}
		
		else if (chunk.name == MakeName("OTRK")) {
			OTRK otrk;
			len -= ReadData(ifd, &otrk, sizeof(OTRK));
			CHECKVERBOSE { fprintf(stdout, "\tOTRK:  %s\n", otrk.Trackname); }
		}
		
		else if (chunk.name == MakeName("OSTR")) {
			CHECKVERBOSE { fprintf(stdout, "\tOSTR:  found\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
		
		else if (chunk.name == MakeName("FADE")) {
			CHECKVERBOSE { fprintf(stdout, "\tFADE:  found\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
		
		else if (chunk.name == MakeName("SKYC")) {
			CHECKVERBOSE { fprintf(stdout, "\tSKYC:  found\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
		
		else if (chunk.name == MakeName("AMBI")) {
			CHECKVERBOSE { fprintf(stdout, "\tAMBI:  found\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
		
		else if (chunk.name == MakeName("GLB0")) {
			CHECKVERBOSE { fprintf(stdout, "\tGLB0:  found\n"); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
		
		else {
			CHECKTERSE { fprintf(stdout, "\t%.4s:  Found unknown chunk\n", &chunk.name); }
			lseek(ifd, chunk.len, 1);
			len -= chunk.len;
		}
	}
}





#define PRINTUSAGE fprintf(stderr,"%s [-q|-t|-v] [-o] [-s scalefactor] infile outfile\n", argv[0]);


void parsecl(int argc, char **argv)
{
	int	i;

	for (i=1; i<argc; i++)
	{
		if (argv[i][0] != '-')
		{
			fprintf(stderr, "%s:  malformed option %s\n", argv[0], argv[i]);
			PRINTUSAGE;
			exit(20);
		}

		switch (argv[i][1])
		{
			case 'v':
				printmode = VERBOSE;
				break;

			case 't':
				printmode = TERSE;
				break;

			case 'q':
				printmode = QUIET;
				break;

			case 'o':
				flags |= FL_OBJECTS;
				break;

			case 's':
				i++;
				scalefactor = atof(argv[i]);
				break;

			default:
				fprintf(stderr, "%s: illegal option %s\n", argv[0], argv[i]);
				PRINTUSAGE;
				exit(20);
		}
	}
}




void main(int argc, char **argv)
{
	int		ifd=-1;
	Form	form;
	Chunk	chunk;
	int		len;
	
	if ((argc < 3) || (argc > 6)) {
		PRINTUSAGE;
		goto cleanup;
	}

	parsecl(argc-2, argv);

	if ((ifd = open(argv[argc==4?2:1], O_RDONLY)) == -1) {
		fprintf(stdout, "turbo2arrt:  could not open input file %s\n", argv[argc==4?2:1]);
		goto cleanup;
	}
	
	if (!(ofh = fopen(argv[argc==4?3:2], "w"))) {
		fprintf(stdout, "turbo2arrt:  could not open output file %s\n", argv[argc==4?3:2]);
		goto cleanup;
	}

	filename = argv[argc==4?2:1];

	read(ifd, &form, sizeof(Form));
	if ((form.form != MakeName("FORM")) || (form.type != MakeName("TDDD"))) {
		fprintf(stdout, "turbo2arrt:  %s is not proper file type\n", argv[1]);
		goto cleanup;
	}
	
	len = read(ifd, &chunk, sizeof(Chunk));
	while (len) {
		if (chunk.name == MakeName("INFO")) {
			CHECKVERBOSE { fprintf(stdout, "INFO:\n"); }
			GetInfoChunk(ifd, chunk.len);
			CHECKVERBOSE { fprintf(stdout, "\n\n"); }
		}
		
		else if (chunk.name == MakeName("OBJ ")) {
			CHECKVERBOSE { fprintf(stdout, "OBJ :\n"); }
			GetObjChunk(ifd, chunk.len);
			CHECKVERBOSE { fprintf(stdout, "\n"); }
		}

		else {
			CHECKTERSE { fprintf(stdout, "%.4s:  Found unknown chunk\n\n\n", &chunk.name); }
			lseek(ifd, chunk.len, 1);
		}

		len = read(ifd, &chunk, sizeof(Chunk));
	}

	CHECKTERSE { fprintf(stdout, "***\n\tObject Bounds: <%f, %f, %f> - <%f, %f, %f>\n", imn[0], imn[2], imn[1], imx[0], imx[2], imx[1]); }

cleanup:

	if (ifd != -1)
		close(ifd);
	if (ofh)
		fclose(ofh);
}
