//#include "stdafx.h"
#include "common.h"


#define RESIZE		100.0f

dword				nummdl;				// number of models
dword				numobj;				// number of objects
dword				numtxt;				// number of textures
dword				numvbuffer;			// number of vertex buffers
dword				mynummat;

dword				objoffsets[256];

byte				*headerdata;		// XPR file header
byte				*xprdata;			// XPR file datablock

FILE				*fpxpr;
xprheader			*xprh;
xprchunk			*xprch;

xprtextureheader	*xprtexture[256];		// all pointers to xpr textures
xprvbufferheader	*xprvbuffer[256];		// all pointers to xpr vertexbuffers
xprmdlheader		*xprmdl[256];			// all pointers to xpr models
xprobjheader		*xprobj[256];			// all pointers to xpr objects
myobjheader			myobj[256];
ddsheader			*dds[256];				// all pointers to DDS headers
ddsobject			ddsobj[256];

FILE				*fpReport;
char filenameheader[32], filenameextension[32];

void HandleMaterial(byte *data);
int RemoveMaterial2(int nObj, int nMaterial);
bool remove_bool=true;



void HandleMaterial(byte *data)
{
	if (!remove_bool) fprintf(fpReport,"\n        Material: %d\n", mynummat);

	myobj[numobj].material[mynummat] = (xprmaterialblock *)data;

//	printf("\tsize: %d\n",myobj[numobj].material[mynummat]->size);
//	printf("\toffset: %x\n",((byte*)(myobj[numobj].material[mynummat]))-headerdata);
	if (!remove_bool) fprintf(fpReport,"\ttex_no: %d\n",myobj[numobj].material[mynummat]->tex_no);

	//other data about the material that isn't really needed here
	/*	fprintf(fpReport,"\tmat_color0:%4.4f,%4.4f,%4.4f,%4.4f\n",myobj[numobj].material[mynummat]->mat_color0[0],
			myobj[numobj].material[mynummat]->mat_color0[1],
			myobj[numobj].material[mynummat]->mat_color0[2],
			myobj[numobj].material[mynummat]->mat_color0[3]);
	fprintf(fpReport,"\tmat_color1:%4.4f,%4.4f,%4.4f,%4.4f\n",myobj[numobj].material[mynummat]->mat_color1[0],
			myobj[numobj].material[mynummat]->mat_color1[1],
			myobj[numobj].material[mynummat]->mat_color1[2],
			myobj[numobj].material[mynummat]->mat_color1[3]);
	fprintf(fpReport,"\tmat_color2:%4.4f,%4.4f,%4.4f,%4.4f\n",myobj[numobj].material[mynummat]->mat_color2[0],
			myobj[numobj].material[mynummat]->mat_color2[1],
			myobj[numobj].material[mynummat]->mat_color2[2],
			myobj[numobj].material[mynummat]->mat_color2[3]);
	fprintf(fpReport,"\tmat_color3:%4.4f,%4.4f,%4.4f,%4.4f\n",myobj[numobj].material[mynummat]->mat_color3[0],
			myobj[numobj].material[mynummat]->mat_color3[1],
			myobj[numobj].material[mynummat]->mat_color3[2],
			myobj[numobj].material[mynummat]->mat_color3[3]);
	fprintf(fpReport,"\tmat_color5:%4.4f,%4.4f,%4.4f,%4.4f\n",myobj[numobj].material[mynummat]->mat_color5[0],
			myobj[numobj].material[mynummat]->mat_color5[1],
			myobj[numobj].material[mynummat]->mat_color5[2],
			myobj[numobj].material[mynummat]->mat_color5[3]);
	fprintf(fpReport,"\n\tpad25: %4.4f\n",myobj[numobj].material[mynummat]->pad25);
	fprintf(fpReport,"\n\tpad26: %d\n",myobj[numobj].material[mynummat]->pad26);

	fprintf(fpReport,"\n\tIndex Buffer:\n\n");
	int numquads=(myobj[numobj].material[mynummat]->size-33)/4;

	dword * pdw=(dword*)(data+33*4);
	for(int i=0;i<numquads;++i)
	{
		fprintf(fpReport,"\t\t%5d,%5d,%5d,%5d\n",pdw[0],pdw[1],pdw[2],pdw[3]);
		pdw+=4;
	}*/
	mynummat++;
}

void HandleObj(byte *data, dword offset)
{
	byte *materialdata;
	mynummat = 0;

	objoffsets[numobj]=offset;

	xprobj[numobj] = (xprobjheader *)(data);


	if (!remove_bool) fprintf(fpReport,"Object %d\n",numobj);
	if (!remove_bool) fprintf(fpReport,"\tnumindices:%d\n",xprobj[numobj]->vi[0].numfaces);
	if (!remove_bool) fprintf(fpReport,"\tnumvertices:%d\n",xprobj[numobj]->vi[0].numverts);
	
	
	
	// collect and process all materials within obj

	materialdata = data + sizeof(xprobjheader);

	while(((((xprmaterialblock *)materialdata)->header) & 0x80000000) == 0x80000000)
	{
		HandleMaterial(materialdata);
		materialdata += (((xprmaterialblock *)materialdata)->size)*4;
	}

	myobj[numobj].numfaces = xprobj[numobj]->numfaces;
	myobj[numobj].nummat = mynummat;

	ddsobj[numobj].numbuckets = mynummat;


	numobj++;
}

void HandleMdl(byte *data)
{
	printf("    Model: %d\n", nummdl);

	xprmdl[nummdl] = (xprmdlheader *)data;

	printf("        Numobjs: %d (dec)\n", xprmdl[nummdl]->numobj);
	printf("        Numtxts: %d (dec)\n", xprmdl[nummdl]->numtxt);

	// collect and process all objects within mdl
	for(dword i=0; i<xprmdl[nummdl]->numobj; i++)
	{
		dword offset = ((dword *)data)[sizeof(xprmdlheader)/4+i];
		HandleObj(headerdata+offset, offset);
	}

	nummdl++;
}

void HandleTexture(byte *data)
{
    dword fmt, dim, u, v, p, levels, cube;

//	printf("    Texture: %d\n", numtxt);

	xprtexture[numtxt] = (xprtextureheader *)data;
//	printf("        Offset: xprheader + %d (dec) = %x\n", xprtexture[numtxt]->offset, xprh->headersize + xprtexture[numtxt]->offset);
//	printf("        Type: %x (hex)\n", xprtexture[numtxt]->type);

	dds[numtxt] = new ddsheader;
	DdsInit(dds[numtxt]);

	// get textureformat
	fmt = ((xprtexture[numtxt]->type) & D3DFORMAT_FORMAT_MASK) >> D3DFORMAT_FORMAT_SHIFT;

	// find textureformat in identifier table
	for(int x=0; textureformats[x].id != 0; x++)
		if(textureformats[x].id == fmt)	break;

	// figure out texture info, fill dds header and write file to disk
	if(textureformats[x].id != 0)
	{
		// set dimensions (2d/3d) levels (mipmaps) and cubemap-identifier
		dim    = ((xprtexture[numtxt]->type) & D3DFORMAT_DIMENSION_MASK) >> D3DFORMAT_DIMENSION_SHIFT;
		levels = ((xprtexture[numtxt]->type) & D3DFORMAT_MIPMAP_MASK) >> D3DFORMAT_MIPMAP_SHIFT;
		cube   = ((xprtexture[numtxt]->type) & D3DFORMAT_CUBEMAP);

		u = exptbl[((xprtexture[numtxt]->type) & D3DFORMAT_USIZE_MASK) >> D3DFORMAT_USIZE_SHIFT];
		v = exptbl[((xprtexture[numtxt]->type) & D3DFORMAT_VSIZE_MASK) >> D3DFORMAT_VSIZE_SHIFT];
		p = exptbl[((xprtexture[numtxt]->type) & D3DFORMAT_PSIZE_MASK) >> D3DFORMAT_PSIZE_SHIFT];

		// set x/y dimensions of image
		dds[numtxt]->u = u;
		dds[numtxt]->v = v;

		// set image-type
		switch(textureformats[x].id)
		{
		case 0x0000000C /*DXT1*/:
			dds[numtxt]->imagesize = (u*v)/2;
			sprintf(dds[numtxt]->ddpffourcc, "%s", "DXT1");
			break;
		case 0x0000000E /*DXT2*/:
			dds[numtxt]->imagesize = (u*v);
			sprintf(dds[numtxt]->ddpffourcc, "%s", "DXT2");
			break;
		case 0x0000000F /*DXT4*/:
			dds[numtxt]->imagesize = (u*v);
			sprintf(dds[numtxt]->ddpffourcc, "%s", "DXT4");
			break;

		default:
			printf("        UNKNOWN TEXTURE FORMAT (not DXT1/DXT2/DXT4)\n");
		}

	}

	numtxt++;
}

void HandleVertexBuffer(byte *data)
{
//	printf("    Vertex buffer: %d\n", numvbuffer);

	xprvbuffer[numvbuffer] = (xprvbufferheader *)data;
//	printf("        Offset: xprdata + %x\n", xprvbuffer[numvbuffer]->offset);

	numvbuffer++;
}

void HandleResource(byte *resource, dword magic)
{
	// handle each resource accordingly
	switch(magic)
	{
		case TEAMNINJATEXTURE: HandleTexture(resource); break;
		case TEAMNINJAVBUFFER: HandleVertexBuffer(resource); break;
		case TEAMNINJAMODEL: HandleMdl(resource); break;
		default: break;
	}
}

void CollectResources()
{
	dword chunksize=0;
	byte *pointer = headerdata + sizeof(xprheader);

	nummdl = 0;
	numobj = 0;
	numtxt = 0;
	numvbuffer = 0;

	// step through the xpr file identifying all resources
	xprch = (xprchunk *)pointer;
	HandleResource(pointer+4, xprch->magic);

	while(
		xprch->magic == TEAMNINJAMODEL ||
		xprch->magic == TEAMNINJATEXTURE ||
		xprch->magic == TEAMNINJAVBUFFER)
	{
		if(xprch->magic == TEAMNINJATEXTURE)	chunksize = sizeof(xprchunk) + sizeof(xprtextureheader);
		if(xprch->magic == TEAMNINJAVBUFFER)	chunksize = sizeof(xprchunk) + sizeof(xprvbufferheader);
		if(xprch->magic == TEAMNINJAMODEL)		chunksize = sizeof(xprchunk) + sizeof(dword) + ((dword *)pointer)[1];

		pointer += chunksize;

		xprch = (xprchunk *)pointer;
		HandleResource(pointer+4, xprch->magic);
	}
}

int XPRReadData(char *filename)
{
	if(!(fpxpr = fopen(filename, "rb"))) { printf("\nNo such file.\n"); return 0; }

	sscanf(filename, "%[^'.'].%[^'.']", &filenameheader, &filenameextension);

	printf("\nProcessing: %s\n\n", filename);

	// get xprheader information
	xprh = new xprheader;
	fread(xprh, sizeof(xprheader), 1, fpxpr);

	// encrypted xprheaders contain bogus information, so check for the real filesize aswell
	int fh = _open(filename, _O_RDONLY);
	dword realfilesize = filelength(fh);

	// and set the pointer back to 0 so we can get the entire xprheader this time
	fseek(fpxpr, 0, SEEK_SET);

	// now, read the header and the data and store them in memoryblocks
    headerdata = new byte[xprh->headersize];
    fread(headerdata, xprh->headersize, 1, fpxpr);

	xprdata = new byte[realfilesize-xprh->headersize];
    fread(xprdata, realfilesize-xprh->headersize, 1, fpxpr);

	fclose(fpxpr);

	return 1;
}

int RemoveMaterial(dword nObj, dword nMaterial)
{
	// this is the original, convoluted attempt at material removal.  it doesn't
	// work
	dword currentoffset=0;
	dword offset;
	// first determine the size of the material that we are removing.
	int nMatSize=(myobj[nObj].material[nMaterial]->size)*4;
	byte * pOutFileBuffer=(byte*)malloc(xprh->filesize);
	memset(pOutFileBuffer,0,xprh->filesize);

	xprh->filesize-=nMatSize;
	xprh->headersize-=nMatSize;

	memcpy(pOutFileBuffer+currentoffset,xprh,sizeof(xprheader));
	currentoffset+=sizeof(xprheader);
	// now we should be able to copy everything up to the model
	dword mdloffset=((byte*)xprmdl[0])-headerdata;
	memcpy(pOutFileBuffer+currentoffset,headerdata+currentoffset,mdloffset-currentoffset);

	currentoffset=mdloffset;
	
	// change the size of the model
	xprmdl[0]->size-=nMatSize;
	byte * data=(byte*)xprmdl[0];
	for(dword i=0; i<xprmdl[0]->numobj; i++)
	{
		if(i>nObj)
		{
			((dword *)data)[sizeof(xprmdlheader)/4+i]-=nMatSize;			
		}
	}
	// now that the size and the offsets are taken care of, we can copy up to the first object
	memcpy(pOutFileBuffer+currentoffset,headerdata+currentoffset,objoffsets[0]-currentoffset);
	currentoffset=objoffsets[0];

	dword origfileoffset;
	for(i=0;i<xprmdl[0]->numobj;i++)
	{
		// if we are before the target object, only the vertex offset needs to change
		if(i<nObj)
		{
			xprobj[i]->vi[0].vertex_offset-=nMatSize;
			memcpy(pOutFileBuffer+currentoffset,headerdata+currentoffset,objoffsets[i+1]-currentoffset);
			currentoffset=objoffsets[i+1];
		}
		else if(i==nObj)
		{
			origfileoffset=currentoffset;
			xprobj[i]->vi[0].vertex_offset-=nMatSize;
			xprobj[i]->vi[0].index_offset-=nMatSize;
			
			data=(byte*)xprobj[i];
			byte *materialdata = data + sizeof(xprobjheader);
			offset=(byte*)xprobj[i]-headerdata;
			memcpy(pOutFileBuffer+currentoffset,headerdata+origfileoffset,sizeof(xprobjheader));
			currentoffset+=sizeof(xprobjheader);
			origfileoffset+=sizeof(xprobjheader);
			int nMatCount=0;
			while(((((xprmaterialblock *)materialdata)->header) & 0x80000000) == 0x80000000)
			{
				if(nMatCount<nMaterial)
				{
					memcpy(pOutFileBuffer+currentoffset,headerdata+origfileoffset,
						((xprmaterialblock *)materialdata)->size*4);
					currentoffset+=((xprmaterialblock *)materialdata)->size*4;
					origfileoffset+=((xprmaterialblock *)materialdata)->size*4;
				}
				else if(nMatCount==nMaterial)
				{
					// this is the material we are skipping.  don't copy it over, and
					// only update the original file offset
					origfileoffset+=((xprmaterialblock *)materialdata)->size*4;
					// we can just copy the rest of this object to the new file now
					if(nObj!=xprmdl[0]->numobj-1)
					{
						memcpy(pOutFileBuffer+currentoffset,headerdata+origfileoffset,objoffsets[i+1]-origfileoffset);
						currentoffset=objoffsets[i+1]-nMatSize;
						origfileoffset=objoffsets[i+1];
					}
					break;  // break out of the while loop
				}
				materialdata += (((xprmaterialblock *)materialdata)->size)*4;
				nMaterial++;
			}
		}
		else if(i>nObj && (i!=xprmdl[0]->numobj-1))
		{
			xprobj[i]->vi[0].vertex_offset-=nMatSize;
			xprobj[i]->vi[0].index_offset-=nMatSize;
			memcpy(pOutFileBuffer+currentoffset,headerdata+origfileoffset,objoffsets[i+1]-origfileoffset);
			currentoffset=objoffsets[i+1]-nMatSize;
			origfileoffset=objoffsets[i+1];
		}
		else if(i==xprmdl[0]->numobj-1)
		{
			xprobj[i]->vi[0].vertex_offset-=nMatSize;
			xprobj[i]->vi[0].index_offset-=nMatSize;
			// this is the last object, we've updated all that we need to update, copy the rest
			// of the file
			memcpy(pOutFileBuffer+currentoffset,headerdata+origfileoffset,xprh->headersize-origfileoffset+nMatSize);
			currentoffset+=xprh->headersize-origfileoffset+nMatSize;
			origfileoffset+=xprh->headersize;
			memcpy(pOutFileBuffer+currentoffset,xprdata,xprh->filesize-xprh->headersize+nMatSize);
		}
	}
	FILE * fp=fopen("out.xpr","wb+");
	fwrite(pOutFileBuffer,1,xprh->filesize,fp);
	return 1;
}

int RemoveMaterial2(dword nObj, dword nMaterial)
{
	byte * pOutFileBuffer=(byte*)malloc(xprh->filesize);
	memset(pOutFileBuffer,0,xprh->filesize);

	// verify the number of objects and materials
	if(nObj >= numobj)
	{
		printf("Invalid object number.  Valid objects are 0 through %d.\n",numobj-1);
		return 0;
	}
	if(nMaterial >= myobj[nObj].nummat)
	{
		printf("Invalid material number.  Valid materials for this object are 0 through %d.\n",myobj[nObj].nummat-1);
		return 0;
	}
	dword indexOffset=0;
	indexOffset=xprobj[nObj]->vi[0].index_offset;
	word* pByte=(word*)(headerdata+indexOffset);
	byte *pMaterial=(byte*)xprobj[nObj]+sizeof(xprobjheader);

	dword * pdw=((dword*)(myobj[nObj].material[nMaterial]))+33;
	int numquads=(myobj[nObj].material[nMaterial]->size-33)/4;

	int skipped=0;
	while(pdw[0]==4||pdw[0]==5)
	{
		pdw+=4;
		++skipped;
	}
	int startIndex=pdw[1];
	pdw+=4*(numquads-1-skipped);
	int endIndex=pdw[1]+pdw[2]+3;
	for(int i=startIndex;i<endIndex;++i)
	{
		pByte[i]=0;
	}
	memcpy(pOutFileBuffer,headerdata,xprh->headersize);
	memcpy(pOutFileBuffer+xprh->headersize,xprdata,xprh->filesize-xprh->headersize);
	
	FILE * fp=fopen("out.xpr","wb+");
	fwrite(pOutFileBuffer,1,xprh->filesize,fp);
	return 1;
}
int RemoveMaterial3(dword nObj, dword nMaterial)
{
	// experimental way to modify/remove materials that involves
	// messing with the vertex buffer.  not really tested yet
	byte * pOutFileBuffer=(byte*)malloc(xprh->filesize);
	memset(pOutFileBuffer,0,xprh->filesize);

	dword indexOffset=0;
	indexOffset=xprobj[nObj]->vi[0].index_offset;
	word* pByte=(word*)(headerdata+indexOffset);
	byte *pMaterial=(byte*)xprobj[nObj]+sizeof(xprobjheader);

	dword * pdw=((dword*)(myobj[nObj].material[nMaterial]))+33;
	int numquads=(myobj[nObj].material[nMaterial]->size-33)/4;

	int skipped=0;
	while(pdw[0]==4||pdw[0]==5)
	{
		pdw+=4;
		++skipped;
	}
	int startIndex=pdw[1];
	pdw+=4*(numquads-1-skipped);
	int endIndex=pdw[1]+pdw[2]+3;
	for(int i=startIndex;i<endIndex;++i)
	{
		dword vertexsize = xprvertexsizes[xprobj[nObj]->vertex_type];
		
		float v1 = RESIZE * ((float *)xprdata+(xprvbuffer[nObj]->offset)/4+vertexsize*i)[0];
		float v2 = RESIZE * ((float *)xprdata+(xprvbuffer[nObj]->offset)/4+vertexsize*i)[1];
		float v3 = RESIZE * ((float *)xprdata+(xprvbuffer[nObj]->offset)/4+vertexsize*i)[2];		
		((float *)xprdata+(xprvbuffer[nObj]->offset)/4+vertexsize*i)[0]/=100.0;
		((float *)xprdata+(xprvbuffer[nObj]->offset)/4+vertexsize*i)[1]/=100.0;
//		((float *)xprdata+(xprvbuffer[nObj]->offset)/4+vertexsize*i)[2]/=10.0;

		//pByte[i]=0;
	}
	memcpy(pOutFileBuffer,headerdata,xprh->headersize);
	memcpy(pOutFileBuffer+xprh->headersize,xprdata,xprh->filesize-xprh->headersize);
	
	FILE * fp=fopen("out.xpr","wb+");
	fwrite(pOutFileBuffer,1,xprh->filesize,fp);
	return 1;
}

int main(int argc, char* argv[])
{
	WIN32_FIND_DATA FileData;
	HANDLE hSearch;
	BOOL bFinished = FALSE;

	if(argc != 2 && argc!=4) { printf("\nUsage: xprremovematerial [file.xpr] <objnumber> <materialnumber>\n"); exit(1);	}
	if (argc==2) { 
		fpReport = fopen("xprReport.txt","wt+");
		remove_bool=false;
	}

	hSearch = FindFirstFile(TEXT(argv[1]), &FileData);
	if (hSearch == INVALID_HANDLE_VALUE) { printf("\nNo files found.\n"); return 0; }

	while(!bFinished)
	{
		if(XPRReadData(FileData.cFileName))
		{
			CollectResources();
			if(argc==4)
				RemoveMaterial2((dword)atoi(argv[2]),(dword)atoi(argv[3]));
		}
		if(!FindNextFile(hSearch, &FileData)) bFinished = TRUE;
	}
	if (argc==2) {
		printf("\nxprReport.txt created");
		fclose(fpReport);
	}
	if (argc==4) printf("\nout.xpr created");
	return 0;
}

