// ChunkDump.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "..\Headers.h"

void ReadBone(FILE *f, BONEANIM_CHUNK_DESC &md, int recurse_no, bool option_all)
{
	//read bone
	BONE_ENTITY ent;
	fread(&ent,sizeof(ent),1,f);
	printf("\n\t"); for(int i=0;i<recurse_no;i++,printf(".")); printf(" ID=%d nchild=%d contID=%d parentID=%d",ent.BoneID, ent.nChildren, ent.ControllerID, ent.ParentID);
	
	//recurse
	for(i=0;i<ent.nChildren;i++)
	{
		ReadBone(f,md,recurse_no+1,option_all);
	}
}

void DumpChunk(FILE *f, CHUNK_HEADER ch, bool option_all)
{
	fseek(f,ch.FileOffset,SEEK_SET);

	switch(ch.ChunkType)
	{
	case ChunkType_Node:
		{
			NODE_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\n%2d: Chunk ChunkType_Node",md.chdr.ChunkID);
			printf("\n========================");
			printf("\n\tVersion    : %04x",md.chdr.ChunkVersion);
			printf("\n\tnodeName   : %s"  ,md.name);
			printf("\n\tObjectChunk: %d"  ,md.ObjectID);
			printf("\n\tMatChunk   : %d"  ,md.MatID);
			printf("\n\tPos        : (%f, %f, %f)"		,md.pos.x, md.pos.y, md.pos.z);
			printf("\n\trot        : (%f, %f, %f, %f)"	,md.rot.x,md.rot.y,md.rot.z, md.rot.w);
			printf("\n\tscl        : (%f, %f, %f)"		,md.scl.x, md.scl.y, md.scl.z);
			printf("\n\tpos cont id: %d"  ,md.pos_cont_id);
			printf("\n\trot cont id: %d"  ,md.rot_cont_id);
			printf("\n\tscl cont id: %d"  ,md.scl_cont_id);
			printf("\n\tParentID   : %d"  ,md.ParentID);
			printf("\n\tIsGrpHead  : %s"  ,md.IsGroupHead? "Yes" : "No");
			printf("\n\tIsGrpMember: %s"  ,md.IsGroupMember? "Yes" : "No");
			printf("\n\tPropStrLen : %d"  ,md.PropStrLen);
			if(md.PropStrLen)
			{
				char *pr_str=(char *)calloc(md.PropStrLen,1);
				fread(pr_str,md.PropStrLen,1,f);
				printf("\n\tPropStr    : %s",pr_str);
				free(pr_str);
			}

			printf("\n\tnChildren  : %d"  ,md.nChildren);
			if(md.nChildren)
			{
				printf("\n\tChildren   : ");
				int child;
				for(int i=0;i<md.nChildren;i++)
				{
					fread(&child,sizeof(int),1,f);
					printf("%d ",child);
				}
			}
		}
		break;	
	
	case ChunkType_Mesh:
		{
			MESH_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\n%2d: Chunk ChunkType_Mesh",md.chdr.ChunkID);
			printf("\n========================");
			printf("\n\tVersion    : %04x",md.chdr.ChunkVersion);
			printf("\n\tBone Info  : %s",md.HasBoneInfo? "Yes" : "No");
			printf("\n\tVert Colors: %s",md.HasVertexCol? "Yes" : "No");
			printf("\n\t# Faces    : %d",md.nFaces);
			printf("\n\t# Vertices : %d",md.nVerts);
			printf("\n\t# TVertices: %d",md.nTVerts);

			if(option_all && md.HasBoneInfo)
			{
				//skip other info
				fseek(f,md.nVerts*sizeof(CryVertex)	,SEEK_CUR);//skip vertex data
				fseek(f,md.nFaces*sizeof(CryFace)	,SEEK_CUR);//skip face data
				if(md.nTVerts)
				{
					fseek(f,md.nTVerts*sizeof(CryUV)	,SEEK_CUR);//skip texture vertices data
					if(md.nTVerts != md.nVerts)
						fseek(f,md.nFaces*sizeof(CryTexFace)	,SEEK_CUR);//skip texture faces data
				}

				printf("\n\tLink Info");
				int BuggyLink=0;

				for(int i=0;i<md.nVerts;i++)
				{
					int nLinks;
					int res=fread(&nLinks, sizeof(nLinks), 1, f);
					printf("\n\t\tVertex %4d: (%d links) =>",i,nLinks);

					float tot=0.0f;
					for(int j=0;j<nLinks;j++)
					{
						CryLink link;
						res=fread(&link, sizeof(link), 1, f);
						printf(" (%d : %f)",link.BoneID,link.Blending);
						tot+=link.Blending;
					}
					if(tot<0.99f){  printf("***"); BuggyLink++; }
				}

				if(BuggyLink) printf("\n\tThere were %d verts (marked with ***) whose total weight is <1.0",BuggyLink);
			}
		}
		break;


	case ChunkType_Helper:
		{
			HELPER_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\n%2d: Chunk ChunkType_Helper",md.chdr.ChunkID);
			printf("\n==========================");
			printf("\n\tVersion    : %04x",md.chdr.ChunkVersion);
			printf("\n\tType       : %s",md.type == HP_DUMMY ? "Dummy" : md.type == HP_POINT ? "Point" : "Group");
			printf("\n\tsize       : (%f, %f, %f)",md.size.x,md.size.y,md.size.z);
		}
		break;

	case ChunkType_Light:
		{
			char type[][64]={"Omni", "Spot", "Direct", "Ambient"};

			LIGHT_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\n%2d: Chunk ChunkType_Light",md.chdr.ChunkID);
			printf("\n=========================");
			printf("\n\tVersion    : %04x"		,md.chdr.ChunkVersion);
			printf("\n\tType       : %s"		,type[md.type]);
			printf("\n\tcol (r,g,b): (%d, %d, %d)",md.color.r,md.color.g,md.color.b);
			printf("\n\tIntensity  : %f"		,md.intens);
			printf("\n\tOn/Off	   : %s"		,md.on ? "On" : "Off");
			printf("\n\tCast Shadow: %s"		,md.shadow ? "Yes" : "No");
			printf("\n\tNear Range : %s (%f, %f)",md.useNearAtten? "Active" : "Inactive",md.nearAttenStart,md.nearAttenEnd);
			printf("\n\tFar Range  : %s (%f, %f)",md.useAtten? "Active" : "Inactive",md.attenStart,md.attenEnd);
			printf("\n\tHotSpot    : %f"		,md.hotsize);
			printf("\n\tFalloff    : %f"		,md.fallsize);
		}
		break;

	case ChunkType_Mtl:
		{
			char type[][64]={"Unknown", "Standard", "Multi", "2 Sided"};

			MTL_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\n%2d: Chunk ChunkType_Mtl",md.chdr.ChunkID);
			printf("\n=======================");
			printf("\n\tVersion    : %04x",md.chdr.ChunkVersion);
			printf("\n\tMat Name   : %s",md.name);
			printf("\n\tType	   : %s",type[md.MtlType]);

			switch (md.MtlType)
			{
			case MTL_STANDARD:
				printf("\n\tDif Col    : (R:%d, G:%d, B:%d)",md.std.col_d.r, md.std.col_d.g, md.std.col_d.b);
				printf("\n\tAmb Col    : (R:%d, G:%d, B:%d)",md.std.col_a.r, md.std.col_a.g, md.std.col_a.b);
				printf("\n\tSpec Col   : (R:%d, G:%d, B:%d)",md.std.col_s.r, md.std.col_s.g, md.std.col_s.b);
				printf("\n\tBounce     : %f",md.std.Dyn_Bounce);
				printf("\n\tStat Frict : %f",md.std.Dyn_StaticFriction);
				printf("\n\tSlid Frict : %f",md.std.Dyn_SlidingFriction);
				printf("\n\tDifuse Map : %s",md.std.tex_d.name);
				if(md.std.tex_d.name[0])
				{
					TextureMap &tex=md.std.tex_d;
					printf("\n\t\tuoff_val (cid) : %f (%d)",tex.uoff_val,tex.uoff_ctrlID);
					printf("\n\t\turot_val (cid) : %f (%d)",tex.urot_val,tex.urot_ctrlID);
					printf("\n\t\tuscl_val (cid) : %f (%d)",tex.uscl_val,tex.uscl_ctrlID);
					printf("\n\t\tvoff_val (cid) : %f (%d)",tex.voff_val,tex.voff_ctrlID);
					printf("\n\t\tvrot_val (cid) : %f (%d)",tex.vrot_val,tex.vrot_ctrlID);
					printf("\n\t\tvscl_val (cid) : %f (%d)",tex.vscl_val,tex.vscl_ctrlID);
					printf("\n\t\twrot_val (cid) : %f (%d)",tex.wrot_val,tex.wrot_ctrlID);
				}
				printf("\n\tBump Map   : %s",md.std.tex_b.name);
				if(md.std.tex_b.name[0])
				{
					TextureMap &tex=md.std.tex_b;
					printf("\n\t\tuoff_val (cid) : %f (%d)",tex.uoff_val,tex.uoff_ctrlID);
					printf("\n\t\turot_val (cid) : %f (%d)",tex.urot_val,tex.urot_ctrlID);
					printf("\n\t\tuscl_val (cid) : %f (%d)",tex.uscl_val,tex.uscl_ctrlID);
					printf("\n\t\tvoff_val (cid) : %f (%d)",tex.voff_val,tex.voff_ctrlID);
					printf("\n\t\tvrot_val (cid) : %f (%d)",tex.vrot_val,tex.vrot_ctrlID);
					printf("\n\t\tvscl_val (cid) : %f (%d)",tex.vscl_val,tex.vscl_ctrlID);
					printf("\n\t\twrot_val (cid) : %f (%d)",tex.wrot_val,tex.wrot_ctrlID);
				}
				printf("\n\tOpac Map   : %s",md.std.tex_o.name);
				if(md.std.tex_o.name[0])
				{
					TextureMap &tex=md.std.tex_o;
					printf("\n\t\tuoff_val (cid) : %f (%d)",tex.uoff_val,tex.uoff_ctrlID);
					printf("\n\t\turot_val (cid) : %f (%d)",tex.urot_val,tex.urot_ctrlID);
					printf("\n\t\tuscl_val (cid) : %f (%d)",tex.uscl_val,tex.uscl_ctrlID);
					printf("\n\t\tvoff_val (cid) : %f (%d)",tex.voff_val,tex.voff_ctrlID);
					printf("\n\t\tvrot_val (cid) : %f (%d)",tex.vrot_val,tex.vrot_ctrlID);
					printf("\n\t\tvscl_val (cid) : %f (%d)",tex.vscl_val,tex.vscl_ctrlID);
					printf("\n\t\twrot_val (cid) : %f (%d)",tex.wrot_val,tex.wrot_ctrlID);
				}
				break;

			case MTL_MULTI:
				printf("\n\t# sub-mtls: %d (",md.multi.nChildren);
				for(int i=0;i<md.multi.nChildren;i++)
				{
					int no;		
					fread(&no,sizeof(no),1,f);
					printf("id%d: %d, ",i,no);
				}
				printf(")");
				break;
			}
		}
		break;

	case ChunkType_VertAnim:
		{
			VERTANIM_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\n%2d: Chunk ChunkType_VertAnim",md.chdr.ChunkID);
			printf("\n============================");
			printf("\n\tVersion    : %04x",md.chdr.ChunkVersion);
			printf("\n\tGeomChunkID: %d",md.GeomID);
			printf("\n\t# Vertices : %d",md.nVerts);
			printf("\n\t# Faces    : %d",md.nFaces);
			printf("\n\t# Keys     : %d",md.nKeys);
			printf("\n");
			for(int i=0;i<md.nKeys;i++)
			{
				KEY_HEADER kh;
				fread(&kh,sizeof(kh),1,f);
				printf("\n\tKey %2d Time: %d",i,kh.KeyTime);

				CryPoint3 p;
				for(int j=0;j<md.nVerts;j++) fread(&p,sizeof(p),1,f);
			}
		}
		break;

	case ChunkType_Controller:
		{
			char type[][64]={"CTRL_NONE", "CTRL_CRYBONE", 
				"CTRL_LINEER1", "CTRL_LINEER3", "CTRL_LINEERQ",
				"CTRL_BEZIER1","CTRL_BEZIER3","CTRL_BEZIERQ",
				"CTRL_TCB1","CTRL_TCB3","CTRL_TCBQ"};

			CONTROLLER_CHUNK_DESC cd;
			fread(&cd,sizeof(cd),1,f);
			printf("\n\n%2d: Chunk ChunkType_Controller",cd.chdr.ChunkID);
			printf("\n==============================");
			printf("\n\tVersion    : %04x",cd.chdr.ChunkVersion);
			printf("\n\tType       : %s",type[cd.type]);
			printf("\n\t# Keys     : %d",cd.nKeys);
		}
		break;

	case ChunkType_SceneProps:
		{
			SCENEPROPS_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\n%2d: Chunk ChunkType_SceneProps",md.chdr.ChunkID);
			printf("\n=========================");
			printf("\n\tVersion    : %04x",md.chdr.ChunkVersion);
			printf("\n\t# Props    : %d",md.nProps);

			for(int i=0;i<md.nProps;i++)
			{
				SCENEPROP_ENTITY prop;
				fread(&prop,sizeof(prop),1,f);
				printf("\n\t %d: %s = %s",i,prop.name, prop.value);
			}
		}
		break;

	case ChunkType_Timing:
		{
			TIMING_CHUNK_DESC td;
			fread(&td,sizeof(td),1,f);
			printf("\n\n%2d: Chunk ChunkType_Timing",td.chdr.ChunkID);
			printf("\n=========================");
			printf("\n\tVersion    : %04x",td.chdr.ChunkVersion);
			printf("\n\tSecsPerTick: %f",td.SecsPerTick);
			printf("\n\tTicksPerFrm: %d",td.TicksPerFrame);
			printf("\n\tglobal strt: %d",td.global_range.start);
			printf("\n\tglobal end : %d",td.global_range.end);
			printf("\n\t# subranges: %d",td.nSubRanges);
			for(int i=0;i<td.nSubRanges;i++)
			{
				RANGE_ENTITY range;
				fread(&range,sizeof(range),1,f);
				printf("\n\t %d: %s = (%d, %d)",i,range.name, range.start, range.end);
			}
		}
		break;	/*
	case ChunkType_BoneAnim:
		{
			BONEANIM_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\nChunk ChunkType_BoneAnim");
			printf("\n==========================");
			printf("\n\tVersion    : %04x",md.ChunkVersion);
			printf("\n\tsecs/Ticks : %f",md.SecsPerTick);
			printf("\n\tTicks/Frame: %d",md.TicksPerFrame);
			printf("\n\tStart frame: %d",md.startframe);
			printf("\n\tEnd frame  : %d",md.endframe);
			
			ReadBone(f,md,0,option_all);
		}
		break;
	*/

	case ChunkType_GeomNameList:
		{
			GEOMNAMELIST_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\nChunk ChunkType_GeomNameList");
			printf("\n===========================");
			printf("\n\tVersion    : %04x",md.ChunkVersion);
			printf("\n\t# Entities: %d\n",md.nEntities);
			for(int i=0;i<md.nEntities;i++)
			{
				NAME_ENTITY me;
				fread(&me,sizeof(me),1,f);
				printf("\n\tID %d: %s",i,me.name);
			}
		}
		break;

	case ChunkType_BoneNameList:
		{
			BONENAMELIST_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\nChunk ChunkType_BoneNameList");
			printf("\n===========================");
			printf("\n\tVersion    : %04x",md.chdr.ChunkVersion);
			printf("\n\t# Entities: %d\n",md.nEntities);
			for(int i=0;i<md.nEntities;i++)
			{
				NAME_ENTITY me;
				fread(&me,sizeof(me),1,f);
				printf("\n\tID %d: %s",i,me.name);
			}
		}	
		break;


	/*
	case ChunkType_MRM:
		{
			MRM_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\nChunk ChunkType_MRM");
			printf("\n====================");
			printf("\n\tVersion    : %04x",md.chdr.ChunkVersion);
			printf("\n\tGeomID     : %d",md.GeomID);
		}
		break;

	case ChunkType_PatchMesh:
		{
			PATCHMESH_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\nChunk ChunkType_PatchMesh");
			printf("\n====================");
			printf("\n\tVersion    : %04x",md.ChunkVersion);
			printf("\n\tGeomID     : %d",md.GeomID);
			printf("\n\tBone Info  : %s",md.HasBoneInfo? "Yes" : "No");
			printf("\n\tSpace      : %s",md.InWorldSpace ? "World" : "Local");
			printf("\n\tMat ID     : %d",md.MatID);
			printf("\n\t# Patches  : %d",md.nPatches);
			printf("\n\t# Vertices : %d",md.nVerts);
			printf("\n\t# TVertices: %d",md.nTVerts);
			printf("\n\tPropStrLen : %d",md.PropStrLen);

			if(md.PropStrLen)
			{
				char *pr_str=(char *)calloc(md.PropStrLen,1);
				fread(pr_str,md.PropStrLen,1,f);
				printf("\n\tPropStr    : %s",pr_str);
				free(pr_str);
			}
		}
		break;

	case ChunkType_MtlList:
		{
			MTLLIST_CHUNK_DESC md;
			fread(&md,sizeof(md),1,f);
			printf("\n\nChunk ChunkType_MtlList");
			printf("\n=========================");
			printf("\n\tVersion    : %04x",md.ChunkVersion);
			printf("\n\t# Materials: %d",md.nEntities);

			for(int i=0;i<md.nEntities;i++)
			{
				MAT_ENTITY me;
				fread(&me,sizeof(me),1,f);
				printf("\n\n\tMat No    : %d",i);
				printf("\n\tMat Name  : %s",me.name);
				printf("\n\tStandard  : %s",me.IsStdMat ? "Yes" : "No");
				printf("\n\tDif Col   : (R:%d,G:%d,B:%d)",me.col_d.r, me.col_d.g, me.col_d.b);
				printf("\n\tAmb Col   : (R:%d,G:%d,B:%d)",me.col_a.r, me.col_a.g, me.col_a.b);
				printf("\n\tSpec Col  : (R:%d,G:%d,B:%d)",me.col_s.r, me.col_s.g, me.col_s.b);
				printf("\n\tDifuse Map: %s",me.map_d);
				printf("\n\tBump Map  : %s",me.map_b);
				printf("\n\tOpac Map  : %s",me.map_o);
				printf("\n\tBounce    : %f",me.Dyn_Bounce);
				printf("\n\tStat Frict: %f",me.Dyn_StaticFriction);
				printf("\n\tSlid Frict: %f",me.Dyn_SlidingFriction);
			}
		}
		break;
	*/


	}
}

int main(int argc, char* argv[])
{
	if(argc!=2 &&  argc!=3)
	{
		printf("\nusage: Dump.exe filename [/all]\n");
		return 0;
	}

	FILE *f=fopen(argv[1],"rb+");
	if(!f)
	{
		printf("\nERROR: can not open file %s",argv[1]);
		return 0;
	}

	bool option_all=false;
	if(argc == 3)
	{
		if(strcmp(argv[2],"/all"))
		{
			printf("\nERROR: invalid option %s",argv[2]);
			return 0;
		}
		option_all=true;
	};

	
	// ================
	//read file header
	// ================
	FILE_HEADER fh;
	fread(&fh,sizeof(fh),1,f);
	char ft[32];
	switch(fh.FileType)
	{
	case FileType_Geom:		strcpy(ft,"FileType_Geom"); break;
	case FileType_Anim:		strcpy(ft,"FileType_Anim"); break;
	}
	printf("\n\nFILE HEADER");
	printf("\n=============");
	printf("\nSignature: %s",fh.Signature);
	printf("\nType     : %s",ft);
	printf("\nVersion  : %x",fh.Version);
	printf("\nCh tabl @: %d",fh.ChunkTableOffset);

	if(fh.Version >= (fh.FileType==FileType_Geom) ? GeomFileVersion : AnimFileVersion)
	{

		// ================
		// Read Chunk Table
		// ================
		fseek(f,fh.ChunkTableOffset,SEEK_SET);
		int n_chunks;
		fread(&n_chunks,sizeof(n_chunks),1,f);
		
		CHUNK_HEADER *ch=(CHUNK_HEADER *)calloc(sizeof(CHUNK_HEADER),n_chunks);
		assert(ch);

		int res=fread(ch,sizeof(CHUNK_HEADER),n_chunks,f);
		assert(res==n_chunks);


		printf("\n\nCHUNK LIST");
		printf("\n=============");
		for(int i=0;i<n_chunks;i++)
		{
			char ft[32];
			switch(ch[i].ChunkType)
			{
			case ChunkType_Mesh:			strcpy(ft,"ChunkType_Mesh"); break;
			case ChunkType_MRM: 			strcpy(ft,"ChunkType_MRM"); break;
			case ChunkType_Helper:			strcpy(ft,"ChunkType_Helper"); break;
			case ChunkType_Light:			strcpy(ft,"ChunkType_Light"); break;
			case ChunkType_VertAnim:		strcpy(ft,"ChunkType_VertAnim"); break;
			case ChunkType_BoneAnim:		strcpy(ft,"ChunkType_BoneAnim"); break;
			case ChunkType_GeomNameList:	strcpy(ft,"ChunkType_GeomNameList"); break;
			case ChunkType_BoneNameList:	strcpy(ft,"ChunkType_BoneNameList"); break;
			case ChunkType_MtlList:			strcpy(ft,"ChunkType_MtlList"); break;
			case ChunkType_SceneProps:		strcpy(ft,"ChunkType_SceneProps"); break;
			case ChunkType_PatchMesh:		strcpy(ft,"ChunkType_PatchMesh"); break;
			case ChunkType_Node:			strcpy(ft,"ChunkType_Node"); break;
			case ChunkType_Mtl:				strcpy(ft,"ChunkType_Mtl"); break;
			case ChunkType_Controller:		strcpy(ft,"ChunkType_Controller"); break;
			case ChunkType_Timing:			strcpy(ft,"ChunkType_Timing"); break;
			default: strcpy(ft,"<<UNKNOWN CHUNK>>");
			}
			printf("\nChunk %d",i);
			printf("\n\tChunk Type :%s",ft);
			printf("\n\tChunk Offs.:%d",ch[i].FileOffset);
		}

		// ================
		// Dump Chunks
		// ================
		for(i=0;i<n_chunks;i++)
		{
			DumpChunk(f, ch[i], option_all);
		}
	}
	else
	{
		printf("\nError: This is a old file version: obsolute");
	}
	
	fclose(f);
	printf("\n");
	getch();
}