/* :ts=8 */

/*
	Data types used in Silver
*/

#define	FAST	register
#define	CARD	unsigned
#define	GLOBAL	extern
#define	FRACT	long

typedef	UBYTE	COLOR[3];

typedef	struct	vectors {
	FRACT	X;
	FRACT	Y;
	FRACT	Z;
} VECTOR;

/*
 *	A "FRACT" number is a 32 bit fixed point number with a 16 bit
 *	fractional part.  (e.g.  7.25 is represented as 0x00074000)
 *
 *	Colors are stored as 3 UBYTE values on a scale of 0 to 255.
 */

/*
	This is the internal structure of Turbo Silver Objects
*/

#define	PRP_SURFACE	0
#define	PRP_BRUSH	PRP_SURFACE + 1
#define	PRP_STENCIL	PRP_BRUSH + 1
#define	PRP_MATTER	PRP_STENCIL + 1
#define	PRP_BLEND	PRP_MATTER + 1
#define	PRP_SMOOTH	PRP_BLEND + 1
#define	PRP_SHINY	PRP_SMOOTH + 1
#define	PRP_EXTRA	PRP_SHINY + 1

#define	NUM_OBJ_PROPS	(PRP_EXTRA + 1)

#define	ABS_ROT	1
#define ABS_SCL 2
#define	LOC_ROT	4
#define	LOC_SCL	8

typedef	struct	story	{
	UBYTE	Path[18];			/*  18 bytes		*/
	VECTOR	Rotate;				/*  12 bytes		*/
	VECTOR	Scale;				/*  12 bytes		*/
	UWORD	info;				/*   2 bytes		*/
} STORY;					/*  44 bytes total	*/

typedef	struct	barfu	{
	UBYTE	Shape;				/*   1 byte		*/
	UBYTE	Parent [3];			/*   3 bytes		*/
	ULONG	Number;				/*   4 bytes		*/
	VECTOR	Position;			/*  12 bytes		*/
	VECTOR	A;				/*  12 bytes		*/
	VECTOR	B;				/*  12 bytes		*/
	VECTOR	C;				/*  12 bytes		*/
	VECTOR	S;				/*  12 bytes		*/
} SUBOBJ, *SUBPTR;				/*  68 bytes total	*/

typedef	struct	object	{
struct	object	*Parent;			/*   4 bytes		*/
struct	object	*Child;				/*   4 bytes		*/
struct	object	*Older;				/*   4 bytes		*/
struct	object	*Younger;			/*   4 bytes		*/
	STORY	*Story;				/*   4 bytes		*/
	SUBOBJ	Object;				/*  68 bytes		*/
	UBYTE	Props [NUM_OBJ_PROPS];		/*   8 bytes		*/
	UBYTE	Name [18];			/*  18 bytes		*/
	UWORD	PCount;				/*   2 bytes		*/
	ULONG	TCount;				/*   4 bytes		*/
	VECTOR	*Points;			/*   4 bytes / 12 bytes	*/
	UWORD	*Connects;			/*   4 bytes /  6 bytes	*/
	SUBOBJ	*Subjects;			/*   4 bytes / 68 bytes	*/
	VECTOR	*Phongs;			/*   4 bytes / 12 bytes	*/
	COLOR	*Diffuse;			/*   4 bytes /  3 bytes	*/
	COLOR	*Reflect;			/*   4 bytes /  3 bytes	*/
	COLOR	*Transmit;			/*   4 bytes /  3 bytes	*/
} OBJECT, *OBJPTR;				/* 152 bytes total	*/

#define	OSIZE	(long) sizeof (OBJECT)
#define	SSIZE	(long) sizeof (SUBOBJ)
#define	VSIZE	(long) sizeof (VECTOR)

#define	SPHERE		0L
#define	STENCIL		1L
#define	AXIS		2L
#define	FACETS		3L
#define	SURFACE		4L

/*
 *	The 'Object' component of the 'struct object' structure
 *	and the 'Props[]' component, contains the necessary info
 *	for everything but faceted objects (i.e. spheres, surfaces,
 *	stencils and axes).  The 'Object.Shape' field is a number
 *	(from the #defines above) specifying which type of object
 *	is described.  The 'Object.Parent' and 'Object.Number' fields
 *	don't matter in file data.  The 'Object.A', 'B', and 'C' vectors
 *	are the unit vectors describing the object coordinate system
 *	('A' is the 'x' axis, 'B' is the 'y' axis, 'C' is the 'z').
 *
 *	The 'Props[]' array is:
 *
 *	Props[PRP_SURFACE]	- bits 0-3 are the "surface type":
 *					0 = Matte, 4 = genlock, 5 = brush
 *				- bit 6 is clear for phong shaded, else set
 *	Props[PRP_BRUSH  ]	- the brush number
 *	Props[PRP_STENCIL]	- the stencil number
 *	Props[PRP_MATTER ]	- type of matter:  0 = Air, 1 = Water,
 *					2 = Glass, 3 = Crystal, 4 = Custom
 *	Props[PRP_BLEND  ]	- blending factor 0-255
 *	Props[PRP_SMOOTH ]	- roughness factor 0-255
 *	Props[PRP_SHINY  ]	- non-zero for shiny objects
 *	Props[PRP_EXTRA  ]	- custom index of refraction 0-255
 *					(if Props[PRP_MATTER] == 4).
 *				  0 correnponds to 1.00, and 255 to 3.55
 *
 *	  The 'Diffuse', 'Reflect', and 'Transmit' fields should really
 *	be unions.  If the object is not a "custom" (points/faces) object,
 *	they are really a pad byte and 3 color bytes (RGB).
 *
 *	  If the object is a "custom" - faceted - object, the 'PCount', and
 *	'TCount' parameters give the number of points and faces respectively.
 *	The point list is pointed to by 'Points', and the face list is
 *	pointed to by 'Connects'.  The point list is a list of 'VECTOR's,
 *	and the face list is a list of UWORD point number triples.  The
 *	Colors for each face are stored in 3 COLOR lists pointed to by
 *	'Diffuse', 'Reflect', and 'Transmit'.  There is one COLOR
 *	structure for each face, in order corresponding to the face list.
 *
 *	The routines below indicate what information actually gets read
 *	or written to the file, and how.
*/

/*
 *	These routines write the Silver object trees to the file:
 *
 *	after opening the file ('ofile'):
 *
 *		fprintf (ofile, "SILVER II\nOBJT\n");
 *		WrtNode (TheObject, ofile);
 *		fclose (ofile);
 *
 *	- where 'TheObject' points to a 'struct object'.
*/

static	ULONG	story_header [] = { 0x53545259, (ULONG) sizeof (STORY) };

void	write_node (p, fp)

OBJECT	*p;
FILE	*fp;

{
	while (p) {
		WrtNode (p, fp);
		p = p->Younger;
	}
	fwrite ("\0", 1, 1, fp);
}

void	WrtNode (p, fp)

OBJECT	*p;
FILE	*fp;

{
FAST	ULONG	i;
FAST	UWORD	*w;
FAST	COLOR	*c;

	if ((long) p <= 0)
		return;

	fwrite ("\001", 1, 1, fp);

	fwrite (p->Name, 18, 1, fp);
	fwrite (&p->Object, (int) SSIZE, 1, fp);
	fwrite (p->Props, NUM_OBJ_PROPS, 1, fp);

	if (p->Story) {
		fwrite (story_header, 8, 1, fp);
		fwrite (p->Story, sizeof (STORY), 1, fp);
	}

	fwrite (&p->PCount, 2, 1, fp);
	fwrite (&p->TCount, 4, 1, fp);

	if (p->PCount) {
		fwrite ("PNTS", 4, 1, fp);
		fwrite (p->Points, (int) VSIZE, p->PCount, fp);
	}

	if (p->TCount) {
		fwrite ("CNRS", 4, 1, fp);
		i = p->TCount * 3L;
		w = p->Connects;
		while (i--)
			fwrite (w++, 2, 1, fp);

		i = p->TCount;
		c = p->Diffuse;
		while (i--)
			fwrite (c++, 3, 1, fp);

		i = p->TCount;
		c = p->Reflect;
		while (i--)
			fwrite (c++, 3, 1, fp);

		i = p->TCount;
		c = p->Transmit;
		while (i--)
			fwrite (c++, 3, 1, fp);
	} else {
		fwrite (&p->Diffuse, 4, 1, fp);
		fwrite (&p->Reflect, 4, 1, fp);
		fwrite (&p->Transmit, 4, 1, fp);
	}

	write_node (p->Child, fp);
}

/*
 *	These read the Silver object trees from the file:
 *
 *	After opening the file ('ofile'):
 *
 *		char	s[256];
 *		OBJECT	*temp;
 *
 *		fgets (s, 256, ofile);
 *		if (strncmp (s, "SILVER II", 9))
 *			goto oerr;
 *
 *		fgets (s, 256, ofile);
 *		if (strncmp (s, "OBJT", 4))
 *			goto oerr;
 *
 *		temp = LodNode (ofile);
 *
*/

OBJECT	*load_node (fp)

FILE	*fp;

{
	OBJECT	*head, *prev, *curr;

	head = 0L;
	prev = 0L;

	for (;;) {
		curr = LodNode (fp);
		if (!curr)
			break;

		if (prev)
			prev->Younger = curr;
		else
			head = curr;
		curr->Older = prev;
		prev = curr;
	}

	return head;
}

OBJECT	*LodNode (fp)

FILE	*fp;

{
	OBJECT	*temp, *curr;
	UBYTE	s;
	ULONG	id;
FAST	ULONG	i;
FAST	UWORD	*w;
FAST	COLOR	*c;

	if (feof (fp) || ferror (fp))
		return NULL;

	if (!(s = getc (fp)))
		return NULL;

	if (!(temp = add_node ((int) FACETS)))
		return NULL;

	fread (temp->Name, 18, 1, fp);
	temp->Name[17] = '\0';
	StringUp (temp->Name);
	fread (&temp->Object, (int) SSIZE, 1, fp);
	fread (temp->Props, NUM_OBJ_PROPS, 1, fp);

	fread (&id, 4, 1, fp);
	if (id == 0x53545259) {
		fread (&id, 4, 1, fp);
		if (temp->Story = AllocMem (id, 0L))
			fread (temp->Story, (int) id, 1, fp);
		else
			fseek (fp, id, 1);
	} else
		fseek (fp, -4L, 1);

	fread (&temp->PCount, 2, 1, fp);
	fread (&temp->TCount, 4, 1, fp);

	if (temp->PCount) {
		fread (&id, 4, 1, fp);
		if (id != 0x504e5453) {
			DelNode (temp);
			return NULL;
		}

		if (temp->Points = AllocMem (temp->PCount * VSIZE, 0L))
			fread (temp->Points, (int) VSIZE, temp->PCount, fp);
		else
			fseek (fp, temp->PCount * VSIZE, 1);
	}

	if (temp->TCount) {
		fread (&id, 4, 1, fp);
		if (id != 0x434e5253) {
			DelNode (temp);
			return NULL;
		}
		if (temp->Connects = AllocMem (temp->TCount * 6L, 0L)) {
			i = temp->TCount * 3;
			w = temp->Connects;
			while (i--)
				fread (w++, 2, 1, fp);
		} else
			fseek (fp, temp->TCount * 6L, 1);
	}

	if (temp->TCount)
		if (temp->Diffuse = AllocMem (temp->TCount * 3L, 0L)) {
			i = temp->TCount;
			c = temp->Diffuse;
			while (i--)
				fread (c++, 3, 1, fp);
		} else
			fseek (fp, temp->TCount * 3L, 1);
	else
		fread (&temp->Diffuse, 4, 1, fp);

	if (temp->TCount)
		if (temp->Reflect = AllocMem (temp->TCount * 3L, 0L)) {
			i = temp->TCount;
			c = temp->Reflect;
			while (i--)
				fread (c++, 3, 1, fp);
		} else
			fseek (fp, temp->TCount * 3L, 1);
	else
		fread (&temp->Reflect, 4, 1, fp);

	if (temp->TCount)
		if (temp->Transmit = AllocMem (temp->TCount * 3L, 0L)) {
			i = temp->TCount;
			c = temp->Transmit;
			while (i--)
				fread (c++, 3, 1, fp);
		} else
			fseek (fp, temp->TCount * 3L, 1);
	else
		fread (&temp->Transmit, 4, 1, fp);

	SetSubParent (temp);

	temp->Child = load_node (fp);

	curr = temp->Child;

	while (curr) {
		curr->Parent = temp;
		curr = curr->Younger;
	}

	return temp;
}

