/*
	MultiGen Creator Peo File Exporter.
	Created by Badger 2/10/00

*/


#include <windows.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <math.h>
#include <limits.h>

#include "mgapiall.h"
#include "resource.h"



#define BLANK_BYTE 171

static FILE *fd;

static int currentVertex;
static int totalVertices;
static int totalNormals;
static int totalPolys;
long nameOffset;

int lastChildOffset;


typedef struct {

	char	names[200][50];
	int		numOfNames;
	int		offset;

} namestruct;


typedef struct {

	char identifier[8];
	int version;
    int nameoffset;          // file name offset
    int filesize;
    int localsize;
    int nPublicMaterials;
    int nLocalMaterials;
    int oPublicMaterials;
    int oLocalMaterials;
    int nPolygonObjects;

} header;

typedef struct {
	int				nameoffset;
	unsigned char	ambient[4];
	unsigned char	diffuse[4];
	unsigned char	specular[4];
	float			kAlpha; // not used
	int				textureOffset;
	unsigned short	flags;
	char			numOfIllumColors;
	char			textureRegistered;  //internal
	int				textureNameRegistered;  // internal
} materialentry;

typedef struct {
	int NormalIndex;
	unsigned short iV0;
	unsigned short iV1;
	unsigned short iV2;
	unsigned short MaterialIndex;
	float s0;
	float t0;
	float s1;
	float t1;
	float s2;
	float t2;
	unsigned short flags;
	unsigned short reserved;// two reserved bytes;

} polyentry;


typedef struct {
	float x;
	float y;
	float z;
	int normalIndex;

} vertexentry;


typedef struct {
	float x;
	float y;
	float z;
	int reserved; // 4 reserved bytes
} normalentry;

typedef struct {
	int nameoffset;
	char nodename[30];
	char flags;
	// some sort of object flag, 1 byte
	unsigned short crcname;	// 16bit crc of name;
	int nPolygons;

	int vertexOffset;
	int normalOffset;
	int polylistOffset;

	int pMother;
	int pDaughter;
	int pSister;

	float localMatrix[16];

	int numOfVertices;
	int numOfNormals;

	vertexentry vertexList[1000];
	normalentry normalList[1000];

	polyentry *polyList[1000];

} polyobject;

typedef struct {

	int numOfPolys;
	int numOfMaterials;
	int numOfPolyObjects;
	
	header	Header;
	polyobject polyObjects[50];
	materialentry   *materials[50];
	namestruct		nameRecord;

	
} main_struct_;

static main_struct_ *mainStruct;
static namestruct writtenNames;




/*============================================================================*\
   GUI Functions
\*============================================================================*/

static int addToMaterialNames( char *string )
{
	int i;
	
	for (i=0; i<mainStruct->nameRecord.numOfNames; i++ )
	{
		if ( !strcmp( string, mainStruct->nameRecord.names[i] ))
			return( i );
	}

	strcpy( mainStruct->nameRecord.names[mainStruct->nameRecord.numOfNames], string );
	mainStruct->nameRecord.numOfNames++;
	
	return( mainStruct->nameRecord.numOfNames-1 );
}


static int writeName( char *string )
{
	long oldPosition = ftell( fd );
	long newPosition = 0;
	int i;

	for (i=0; i<writtenNames.numOfNames; i++ )
	{
		if ( !strcmp( string, writtenNames.names[i] ))
			return( writtenNames.offset );
	}

	strcpy( writtenNames.names[writtenNames.numOfNames], string );
	writtenNames.offset = nameOffset;
	writtenNames.numOfNames++;

		fseek( fd, nameOffset, SEEK_SET );
	newPosition = nameOffset;

	fputs( string, fd );
	fputc( '\0', fd );
	nameOffset = ftell( fd );
	mgSendMessage ( MMSG_STATUS, " writing new name %d   %s", newPosition, string );

	fseek( fd, oldPosition, SEEK_SET );

	return( newPosition );

}

static int addToMaterialList( materialentry Mat, polyobject *PO )
{
	int i = 0;
	int index = 0;
	int current = -1;

	for ( i=0; i<mainStruct->numOfMaterials; i++ ) // find if already present
	{
		if (	( mainStruct->materials[i]->ambient[0] == Mat.ambient[0] ) &&
				( mainStruct->materials[i]->ambient[1] == Mat.ambient[1] ) &&
				( mainStruct->materials[i]->ambient[2] == Mat.ambient[2] ) &&
				( mainStruct->materials[i]->diffuse[0] == Mat.diffuse[0] ) &&
				( mainStruct->materials[i]->diffuse[1] == Mat.diffuse[1] ) &&
				( mainStruct->materials[i]->diffuse[2] == Mat.diffuse[2] ) &&
				( mainStruct->materials[i]->specular[0] == Mat.specular[0] ) &&
				( mainStruct->materials[i]->specular[1] == Mat.specular[1] ) &&
				( mainStruct->materials[i]->specular[2] == Mat.specular[2] ) &&
				( mainStruct->materials[i]->textureOffset == Mat.textureOffset ) &&
				( mainStruct->materials[i]->flags == Mat.flags ))
		{	
				current = i;
		}
	}
	
	if ( current == -1 )  // add new entry
	{
	mgSendMessage ( MMSG_STATUS, " creating new material %d %d", mainStruct->numOfMaterials, Mat.textureOffset );
		mainStruct->materials[mainStruct->numOfMaterials] = (materialentry*) mgMalloc ( sizeof(materialentry) );
 
		mainStruct->materials[mainStruct->numOfMaterials]->ambient[0] = Mat.ambient[0];
		mainStruct->materials[mainStruct->numOfMaterials]->ambient[1] = Mat.ambient[1];
		mainStruct->materials[mainStruct->numOfMaterials]->ambient[2] = Mat.ambient[2];
		mainStruct->materials[mainStruct->numOfMaterials]->diffuse[0] = Mat.diffuse[0];
		mainStruct->materials[mainStruct->numOfMaterials]->diffuse[1] = Mat.diffuse[1];
		mainStruct->materials[mainStruct->numOfMaterials]->diffuse[2] = Mat.diffuse[2];
		mainStruct->materials[mainStruct->numOfMaterials]->specular[0] = Mat.specular[0];
		mainStruct->materials[mainStruct->numOfMaterials]->specular[1] = Mat.specular[1];
		mainStruct->materials[mainStruct->numOfMaterials]->specular[2] = Mat.specular[2];
		mainStruct->materials[mainStruct->numOfMaterials]->textureOffset = Mat.textureOffset;
		mainStruct->materials[mainStruct->numOfMaterials]->flags = Mat.flags;
		
		index = mainStruct->numOfMaterials;
		mainStruct->numOfMaterials++;
	}
	else
		index = current;

		return( index );
}


static int addToNormalList( normalentry Norm, polyobject *PO )
{
	int i = 0;
	int index = 0;
	int current = -1;


	for ( i=0; i<PO->numOfNormals; i++ ) // find if already present
	{
		if (( PO->normalList[i].x == Norm.x ) && 
				( PO->normalList[i].y == Norm.y ) &&
				( PO->normalList[i].z == Norm.z ))
		current = i;
	}
	
	if ( current == -1 )  // add new entry
	{
		PO->numOfNormals++;

		PO->normalList[PO->numOfNormals-1].x = Norm.x;	
		PO->normalList[PO->numOfNormals-1].y = Norm.y;	
		PO->normalList[PO->numOfNormals-1].z = Norm.z;	
		index = PO->numOfNormals-1;
		totalNormals++;
	}
	else
		index = current;

		return( index );
}

static int addToVertexList( vertexentry Vert, polyobject *PO )
{
	int i = 0;
	int index = 0;
	int current = -1;


	for ( i=0; i<PO->numOfVertices; i++ ) // find if already present
	{
		if (( PO->vertexList[i].x == Vert.x ) && 
				( PO->vertexList[i].y == Vert.y ) &&
				( PO->vertexList[i].z == Vert.z ))
		current = i;
	}
	
	if ( current == -1 )  // add new entry
	{
		PO->numOfVertices++;

		PO->vertexList[PO->numOfVertices-1].x = Vert.x;	
		PO->vertexList[PO->numOfVertices-1].y = Vert.y;	
		PO->vertexList[PO->numOfVertices-1].z = Vert.z;	
		PO->vertexList[PO->numOfVertices-1].normalIndex = Vert.normalIndex;	
		index = PO->numOfVertices-1;
		totalVertices++;
	}
	else
		index = current;

		return( index );
}

static void computeFaceNormals( void )
{
	float tempvec[3];
	normalentry facenormal;
	int i, j;
	polyentry *poly;
	vertexentry	*v1, *v2, *v3;
	float dist;



	for ( i=0; i<mainStruct->numOfPolyObjects; i++ )
	{
		for ( j=0; j<mainStruct->polyObjects[i].nPolygons; j++ )
		{
			poly = mainStruct->polyObjects[i].polyList[j];
			v1 = &mainStruct->polyObjects[i].vertexList[poly->iV0];
			v2 = &mainStruct->polyObjects[i].vertexList[poly->iV1];
			v3 = &mainStruct->polyObjects[i].vertexList[poly->iV2];
			
			tempvec[0] = v1->x + v2->x + v3->x; 
			tempvec[1] = v1->y + v2->y + v3->y; 
			tempvec[2] = v1->z + v2->z + v3->z; 
			dist = (float)sqrt(( tempvec[0]*tempvec[0] )+( tempvec[1]*tempvec[1] )+( tempvec[2]*tempvec[2] ));
			facenormal.x = tempvec[0]/dist;
			facenormal.y = tempvec[1]/dist;
			facenormal.z = tempvec[2]/dist;
			poly->NormalIndex = addToNormalList( facenormal, &mainStruct->polyObjects[i] );
		}
	}
}

static void readFltVertex( mgrec* rec )
{
	polyobject *PO;
	polyentry *PE;
	vertexentry VE;
 	normalentry NE;
    mgcoord3d pnt1;
	int index;
	float heading, pitch, roll;
	float u_coord, v_coord;

	u_coord = 0;
	v_coord = 0;

	PO = &mainStruct->polyObjects[ mainStruct->numOfPolyObjects-1 ];
	PE = PO->polyList[PO->nPolygons-1];

    mgGetCoord3d ( rec, fltCoord3d, &pnt1.x, &pnt1.y, &pnt1.z );
    mgGetVector ( rec, fltVNormal, &heading, &pitch, &roll );
	mgGetAttList( rec, fltVU, &u_coord, fltVV, &v_coord, MG_NULL );

	NE.x = heading;
	NE.y = pitch;
	NE.z = roll;


	v_coord = v_coord*-1.0f;

//	VE.normalIndex = addToNormalList( NE, PO );
	VE.normalIndex = 0;
	VE.x = (float)pnt1.x;
	VE.y = (float)pnt1.y;
	VE.z = (float)pnt1.z;

	if ( currentVertex == 0 )
	{
		PE->iV0 = addToVertexList( VE, PO );
		PE->s0 = u_coord;
		PE->t0 = v_coord;
		currentVertex++;
		index = PE->iV0;
	}
	else if ( currentVertex == 1 )
	{
		PE->iV1 = addToVertexList( VE, PO );
		PE->s1 = u_coord;
		PE->t1 = v_coord;
		currentVertex++;
		index = PE->iV1;
	}
	else if ( currentVertex == 2 )
	{
		PE->iV2 = addToVertexList( VE, PO );
		PE->s2 = u_coord;
		PE->t2 = v_coord;
		currentVertex++;
		index = PE->iV2;
	}

//	mgSendMessage ( MMSG_STATUS, " U coord %f  V coord %f ", u_coord, v_coord );

//	mgSendMessage ( MMSG_STATUS, "Finished Vertex %s %lf %lf %lf index %d", 
//		mgGetName(rec), VE.x, VE.y, VE.z, index);
//	mgSendMessage ( MMSG_STATUS, "Finished Normal %s %lf %lf %lf index %d", 
//		mgGetName(rec), heading, pitch, roll, VE.normalIndex );

}

static void readFltPolygon( mgrec* rec, mgrec* db_rec )
{
	polyobject *PO;
	polyentry *PE;
	short	polyTexture;
	char *tname;
	char *pos;
	materialentry	Mat;
	int pcolor;
	short r, g, b;
	float	pintens;
	char	drawtype;

	PO = &mainStruct->polyObjects[ mainStruct->numOfPolyObjects-1 ];
	PO->nPolygons++;
	PO->polyList[PO->nPolygons-1] = (polyentry*) mgMalloc ( sizeof(polyentry) );
	if ( PO->polyList[PO->nPolygons-1] == NULL )
	{
		mgSendMessage ( MMSG_STATUS, "polygon entry not allocated" );
		return;
	}	

	PE = PO->polyList[PO->nPolygons-1];

	mgGetAttList( rec, fltPolyTexture, &polyTexture, 
		fltPolyPrimeColor, &pcolor, 
		fltPolyPrimeIntensity, &pintens,
		fltPolyDrawType, &drawtype, MG_NULL );
	tname = mgGetTextureName(db_rec, polyTexture);

	mgIndex2RGB ( db_rec, pcolor, pintens, &r, &g, &b );

	pos = strrchr( tname, '.' );
	if ( pos != NULL )
		*pos = 0;

	pos = strrchr( tname, '/' );
	if ( pos != NULL )
		tname = pos+1;

//	mgSendMessage ( MMSG_STATUS, "texture is %hd  name %s", polyTexture, tname );
//	mgSendMessage ( MMSG_STATUS, "color is %hd %hd %hd ", r, g, b );
	
	Mat.ambient[0] = r;
	Mat.ambient[1] = g;
	Mat.ambient[2] = b;
	Mat.ambient[3] = 0;

	Mat.diffuse[0] = r;
	Mat.diffuse[1] = g;
	Mat.diffuse[2] = b;
	Mat.diffuse[3] = 0;

	Mat.specular[0] = 0;
	Mat.specular[1] = 0;
	Mat.specular[2] = 0;
	Mat.specular[3] = 0;

	Mat.textureOffset = addToMaterialNames( tname );

	Mat.flags = 0;

	if ( drawtype == 1 )
		Mat.flags = 8;  // two sided polys

//	mgSendMessage ( MMSG_STATUS, "material is %d", Mat.flags );
	
	PE->flags = 0;
	PE->iV0 = 0;
	PE->iV1 = 0;
	PE->iV2 = 0;
	PE->MaterialIndex = addToMaterialList( Mat, PO );
	PE->NormalIndex = 0;
	PE->s0 = 0.0f;
	PE->t0 = 0.0f;
	PE->s1 = 0.0f;
	PE->t1 = 0.0f;
	PE->s2 = 0.0f;
	PE->t2 = 0.0f;
	PE->reserved = 0;
	currentVertex = 0;

	totalPolys++;

}


static void readFltObject( mgrec* rec )
{
	polyobject *PO;
	mgrec* xForm;
    mgxfllcode xType;
	mgmatrix matrix;



	PO = &mainStruct->polyObjects[ mainStruct->numOfPolyObjects ];
	
	strcpy( PO->nodename, mgGetName(rec) );

	PO->nameoffset = 1026;
	PO->flags = 2;

	PO->nPolygons = 0;

	PO->vertexOffset = 0;
	PO->normalOffset = 0;
	PO->polylistOffset = 0;
	PO->numOfVertices = 0;
	PO->numOfNormals = 0;

	PO->pMother = 0;
	PO->pDaughter = 0;
	PO->pSister = 0;

	PO->localMatrix[0] = 1.0f;
	PO->localMatrix[1] = 0.0f;
	PO->localMatrix[2] = 0.0f;
	PO->localMatrix[3] = 0.0f;

	PO->localMatrix[4] = 0.0f;
	PO->localMatrix[5] = 1.0f;
	PO->localMatrix[6] = 0.0f;
	PO->localMatrix[7] = 0.0f;

	PO->localMatrix[8] = 0.0f;
	PO->localMatrix[9] = 0.0f;
	PO->localMatrix[10] = 1.0f;
	PO->localMatrix[11] = 0.0f;

	PO->localMatrix[12] = 0.0f;
	PO->localMatrix[13] = 0.0f;
	PO->localMatrix[14] = 0.0f;
	PO->localMatrix[15] = 1.0f;

    if ( mgHasXform ( rec ) )
    {
		xForm = mgGetXform ( rec );
		xType = mgGetXformType ( xForm );
		xType = mgGetXformType ( xForm );
		if ( xType == MXLL_PUT )
		{
			mgSendMessage ( MMSG_STATUS, "Obj \"%s\" has put matrix ", mgGetName(rec) );
			mgGetAttBuf ( rec, fltMatrix, &matrix );

//			mgSendMessage ( MMSG_STATUS, "Obj \"%s\" m0 %lf m1 %lf m2 %lf", 
//					mgGetName(rec), matrix[0], matrix[1], matrix[2] );

			PO->localMatrix[0] = (float)matrix[0];
			PO->localMatrix[1] = (float)matrix[1];
			PO->localMatrix[2] = (float)matrix[2];
			PO->localMatrix[3] = (float)matrix[3];

			PO->localMatrix[4] = (float)matrix[4];
			PO->localMatrix[5] = (float)matrix[5];
			PO->localMatrix[6] = (float)matrix[6];
			PO->localMatrix[7] = (float)matrix[7];

			PO->localMatrix[8] = (float)matrix[8];
			PO->localMatrix[9] = (float)matrix[9];
			PO->localMatrix[10] = (float)matrix[10];
			PO->localMatrix[11] = (float)matrix[11];

			PO->localMatrix[12] = (float)matrix[12];
			PO->localMatrix[13] = (float)matrix[13];
			PO->localMatrix[14] = (float)matrix[14];
			PO->localMatrix[15] = (float)matrix[15];
			
		}
	}
	if ( mainStruct->numOfPolyObjects == 1 )
		mainStruct->polyObjects[ mainStruct->numOfPolyObjects-1 ].pDaughter = 180;
	if ( mainStruct->numOfPolyObjects > 0 )
		PO->pMother = 68;
	if ( mainStruct->numOfPolyObjects > 1 )
		mainStruct->polyObjects[ mainStruct->numOfPolyObjects-1 ].pSister = 68+(112*mainStruct->numOfPolyObjects);
		
		mainStruct->numOfPolyObjects++;

	mgSendMessage ( MMSG_STATUS, "Loaded Obj \"%s\"", mgGetName(rec) );

}

static mgbool preVisitEachNode( mgrec* db_rec, mgrec* par_rec, mgrec* rec, void* id )
{
	char *name;
	name = mgGetName( rec );
	

	if ( mgIsCode( rec, fltObject ) )
	{
		readFltObject( rec );
	}
	else if ( mgIsCode( rec, fltPolygon ) )
	{
		readFltPolygon( rec, db_rec );
	}
	else if ( mgIsCode( rec, fltVertex ) )
	{
		readFltVertex( rec );
	}

	mgFree( name );

	return( MG_TRUE );
}

static void writeNormalList( void )
{
	int i, j;
	long position = 0;

	for ( i=0; i<mainStruct->numOfPolyObjects; i++ )
	{
		position = ftell( fd );
		mainStruct->polyObjects[i].normalOffset = position;
	
		for (j=0; j<mainStruct->polyObjects[i].numOfNormals; j++ )
			fwrite( &mainStruct->polyObjects[i].normalList[j], sizeof( normalentry ), 1, fd );
	}
		mgSendMessage ( MMSG_STATUS, "Written Normals " );
}

static void writeVertexList( void )
{
	int i, j;
	long position = 0;
	vertexentry *entry;

	for ( i=0; i<mainStruct->numOfPolyObjects; i++ )
	{
		position = ftell( fd );
		mainStruct->polyObjects[i].vertexOffset = position;
	
		for (j=0; j<mainStruct->polyObjects[i].numOfVertices; j++ )
		{
			entry = &mainStruct->polyObjects[i].vertexList[j];
			fwrite( entry, sizeof( vertexentry ), 1, fd );			
		}
	}
		mgSendMessage ( MMSG_STATUS, "Written Vertices " );
}

static void writeMaterials( void )
{
	int i;

	mainStruct->Header.oPublicMaterials = ftell( fd );
	mainStruct->Header.oLocalMaterials = ftell( fd );

	for ( i=0; i<mainStruct->numOfMaterials; i++ )
	{
/*		mainStruct->materials[i] = (materialentry*) mgMalloc ( sizeof(materialentry) );

		mainStruct->materials[i]->ambient[0] = 50;
		mainStruct->materials[i]->ambient[1] = 50;
		mainStruct->materials[i]->ambient[2] = 50;
		mainStruct->materials[i]->ambient[3] = 0;
		mainStruct->materials[i]->diffuse[0] = 200;
		mainStruct->materials[i]->diffuse[1] = 200;
		mainStruct->materials[i]->diffuse[2] = 200;
		mainStruct->materials[i]->diffuse[3] = 0;
		mainStruct->materials[i]->specular[0] = 0;
		mainStruct->materials[i]->specular[1] = 0;
		mainStruct->materials[i]->specular[2] = 0;
		mainStruct->materials[i]->specular[3] = 0;
		mainStruct->materials[i]->kAlpha = 0.0f;
		mainStruct->materials[i]->textureOffset;
		mainStruct->materials[i]->flags = 0;
*/		

		mgSendMessage ( MMSG_STATUS, "ambient out %hd %hd %hd %hd", 
			mainStruct->materials[i]->ambient[0], mainStruct->materials[i]->ambient[1],
			mainStruct->materials[i]->ambient[2], mainStruct->materials[i]->ambient[3]);
		
		mainStruct->materials[i]->textureOffset = writeName( 
				mainStruct->nameRecord.names[mainStruct->materials[i]->textureOffset] );

		fwrite( mainStruct->materials[i], sizeof( materialentry ), 1, fd );
	}

}


static void writePolygon( polyentry *poly )
{

	fwrite( poly, sizeof( polyentry ), 1, fd );

}

static void writePolygons( void )
{

	int i, j;
	long position = 0;

	for ( i=0; i<mainStruct->numOfPolyObjects; i++ )
	{
		position = ftell( fd );
		mainStruct->polyObjects[i].polylistOffset = position;
	
		for ( j=0; j<mainStruct->polyObjects[i].nPolygons; j++ )
			writePolygon( mainStruct->polyObjects[i].polyList[j] );
	
	}
		mgSendMessage ( MMSG_STATUS, "Written Polygons " );
}

static void writePolygonObjects( void )
{

	int i, j;

	for ( i=0; i<mainStruct->numOfPolyObjects; i++ )
	{
		mainStruct->polyObjects[i].nameoffset = writeName( mainStruct->polyObjects[i].nodename );
		_putw( mainStruct->polyObjects[i].nameoffset, fd );
		
		fputc( 0, fd );  // internal use 4 bytes
		fputc( 0, fd );
		fputc( 0, fd );
		fputc( 0, fd );
		
		_putw( mainStruct->polyObjects[i].numOfVertices, fd );
		_putw( mainStruct->polyObjects[i].numOfNormals, fd );  // face normals
		_putw( 0, fd );   // vertex normals
		_putw( mainStruct->polyObjects[i].nPolygons, fd );

		_putw( mainStruct->polyObjects[i].vertexOffset, fd );
		_putw( mainStruct->polyObjects[i].normalOffset, fd );
		_putw( mainStruct->polyObjects[i].polylistOffset, fd );
		
		_putw( mainStruct->polyObjects[i].pMother, fd );
		_putw( mainStruct->polyObjects[i].pDaughter, fd );
		_putw( mainStruct->polyObjects[i].pSister, fd );
	
		for (j=0; j<16; j++ )
			fwrite( &mainStruct->polyObjects[i].localMatrix[j], sizeof(float), 1, fd );
		
		
		mgSendMessage ( MMSG_STATUS, "Written Polygonobject \"%d\"", mainStruct->polyObjects[i].numOfVertices);
	}
}


static void writeHeader( void )
{
	int i = 0;

	mainStruct->Header.identifier[0] = 'R';
	mainStruct->Header.identifier[1] = 'M';
	mainStruct->Header.identifier[2] = 'F';
	mainStruct->Header.identifier[3] = '9';
	mainStruct->Header.identifier[4] = '9';
	mainStruct->Header.identifier[5] = 'b';
	mainStruct->Header.identifier[6] = 'a';
	mainStruct->Header.identifier[7] = 0;

	mainStruct->Header.version = 1026;
	mainStruct->Header.nameoffset = 0;
	mainStruct->Header.filesize = nameOffset;
	mainStruct->Header.localsize = 0;
	mainStruct->Header.nPublicMaterials = mainStruct->numOfMaterials;
	mainStruct->Header.nLocalMaterials = 0;
	mainStruct->Header.nPolygonObjects = mainStruct->numOfPolyObjects;

	fputc( mainStruct->Header.identifier[0], fd );
	fputc( mainStruct->Header.identifier[1], fd );
	fputc( mainStruct->Header.identifier[2], fd );
	fputc( mainStruct->Header.identifier[3], fd );
	fputc( mainStruct->Header.identifier[4], fd );
	fputc( mainStruct->Header.identifier[5], fd );
	fputc( mainStruct->Header.identifier[6], fd );
	fputc( mainStruct->Header.identifier[7], fd );

	_putw( mainStruct->Header.version, fd );
	_putw( mainStruct->Header.nameoffset, fd );
	_putw( mainStruct->Header.filesize, fd );
	_putw( mainStruct->Header.localsize, fd );
	_putw( mainStruct->Header.nPublicMaterials, fd );
	_putw( mainStruct->Header.nLocalMaterials, fd );
	_putw( mainStruct->Header.oPublicMaterials, fd );
	_putw( mainStruct->Header.oLocalMaterials, fd );
	_putw( mainStruct->Header.nPolygonObjects, fd );

	for (i=0; i<24; i++ )
		fputc( BLANK_BYTE, fd );

	mgSendMessage ( MMSG_STATUS, "Written Header " );
}


static mgstatus StartWritePeo ( mgplugintool pluginTool, void *userData, void *callData )
{
   mgexportercallbackrec* cbData = (mgexportercallbackrec*) callData;
   mgresource resource = (mgresource) userData;
   mgrec* db = mgGetActivationDb (cbData->toolActivation);
   char *filename = cbData->fileName;

 	mainStruct = (main_struct_*) mgMalloc ( sizeof(main_struct_) );

	if ( mainStruct == NULL )
	{
		mgSendError ( pluginTool, "mainStruct not allocated" );
		return(MSTAT_OK);
	}	


	mainStruct->numOfPolys = 0;
	mainStruct->numOfMaterials = 0;
	mainStruct->numOfPolyObjects = 0;

	mainStruct->nameRecord.numOfNames = 0;
	writtenNames.numOfNames = 0;

	totalVertices = 0;
	totalNormals = 0;
	totalPolys = 0;
	lastChildOffset = 0;


	if ( ( fd = fopen ( filename, "wb" ) ) == NULL ) 
	{
      mgSendError ( pluginTool, "Cannot create \"%s\"", filename );
      return(MSTAT_OK);
	}


	mgWalk( db, preVisitEachNode, MG_NULL, MG_NULL, MWALK_VERTEX );

	computeFaceNormals();

	nameOffset = 68+(112*mainStruct->numOfPolyObjects)+
			(16*totalVertices)+(16*totalNormals)+(40*totalPolys)+(32*mainStruct->numOfMaterials);


	// skip bytes for the rest of the file  header+numObjects*objsize
	fseek( fd, (68+(112*mainStruct->numOfPolyObjects)), SEEK_SET );
	mgSendMessage ( MMSG_STATUS, "begin verts at \"%ld\"", ftell( fd ));
	
	
	writeVertexList();
	mgSendMessage ( MMSG_STATUS, "begin normals at \"%ld\"", ftell( fd ));
	writeNormalList();
	mgSendMessage ( MMSG_STATUS, "begin polygons at \"%ld\"", ftell( fd ));
	writePolygons();
	mgSendMessage ( MMSG_STATUS, "end polygons at \"%ld\"", ftell( fd ));

	writeMaterials();
	mgSendMessage ( MMSG_STATUS, "end materials at \"%ld\"", ftell( fd ));

	fseek( fd, 68, SEEK_SET );
	mgSendMessage ( MMSG_STATUS, "begin poly object at \"%ld\"", ftell( fd ));

	writePolygonObjects();
	mgSendMessage ( MMSG_STATUS, "end poly object at \"%ld\"", ftell( fd ));

	fseek( fd, 0, SEEK_SET );
	mgSendMessage ( MMSG_STATUS, "Setting position at \"%ld\"", ftell( fd ));
	// dont forget to get the filesize
	writeHeader();

	fclose( fd );
	mgSendMessage ( MMSG_STATUS, "Written file <%s>", filename);

   return (MSTAT_OK);
}

mgbool InitWritePeo ( mgplugin plugin, mgresource resource, int* argc, char* argv [] )
{
   mgplugintool pluginTool;
   
   pluginTool = mgRegisterExporter (
      plugin, "Write Peo",
      StartWritePeo, MG_NULL,
	  MTA_VERSION, "1.0",
	  MTA_FILTER, "*.peo",
	  MTA_FILETYPE, "Homeworld Peo Files",
	  MG_NULL );

   return ( pluginTool ? MG_TRUE : MG_FALSE );
}

void ExitWritePeo ( mgplugin plugin )
{
}

