// **********************************************************************************
// * "makeanim.c"																	*
// * Written by :	Sébastien Rubens												*
// * Tabulations :	4 char															*
// **********************************************************************************
#define	MAKEANIM_C

// **********************************************************************************
// Suivi des versions
// 1.01 : 17/07/98 
//		Sort channel by channel number
//		Hierarchy now contains channel number (no more index in array of element)
// 1.02 : 21/07/98
//		Bug correction in Hierarchy optimisation (when channel is supressed)
// 1.03 : 23/07/98
//		Correction in Hierarchy optimisation : kill couple when channel number is undefined
//		Remove Empty Error File
// 1.04 : 30/07/98
//		Use a second Dir(found in Version.ini) to find Anim and generate A3i 
// 1.05 : 17/08/98
//		Generalized use of N Dir found in Version.ini separate by a DIR_SEPARATOR 
//		Treat CameraAngle Key in General Section of A3D
// 1.05b : 25/08/98
//		Raise stack of event
// 1.06 : 25/08/98
//		Generate All anim in a specific dir (first dir in version.ini)
//		To Generate All anim use option -a
// 1.07 : 27/08/98
//		Change Morph structure (like N64)
//		Treat Morphing Entry in Key Section
// 1.08 : 28/08/98
//		Accelerated Interpollation - Type 1
//		Put Version in A3i File
// 1.09 : 11/09/98
//		Correction of overwrite bug with anim with 0 channel
//		Transparency in animation is now possible (transparency isn't interpolated)
// 1.09a : 16/09/98
//		Correction Add the good number of Alignement bytes before events 
// 1.09b : 24/09/98
//		Correction of overwrite bug with anim with 0 channel (previous correction was lost)
// 1.09c : 25/09/98
//		Merging end + correction from Seb Rubens
// 1.10  : 29/09/98
//		Ajout du numero de canal dans les events et tri complet des events
//		(correction bug tir et arret des sons)
// 1.10a : 29/09/98
//		Bug correction : when last key is a simple key and wasn't on the last frame channel
//			don't move after the last key
// 1.11  : 09/10/98
//		Correction for scale computing
//         14/10/98
//		New Option -z : to compact TBL (save original in a TBO file)
//		All families in the file MakeAnim.in are not compacted
//		MakeAnim.ini format:
//			FamilyName1<CR>
//			...
//			FamilyNameN<CR>
// 1.11a : 14/10/98
//		Correction of transparence bug (in NTTO optimisation)
// 1.11b : 16/10/98
//		Bug correction for object number > 255
//		Bug correction, now deleting unused event 
// 1.12  : 19/10/98
//		Integration of the last change from Seb
// 1.13  : 22/10/98 FBF
//		Integration modif from N64 version	
// 1.13a : 26/10/98 
//		Bug correction when having V5 Anim (respect anim indexing)
//		Add precision parameter
//		Correction for emptyobject optimization
//		Correction for Generate Channel object
// 1.13b : 27/10/98 
//		Correction for quaternion interpolation
// 1.13c : 28/10/98 
//		Correction with bad A3D file or Empty file (respect anim indexing)
// 1.13d : 30/10/98 
//		Sort of Warnings and Errors 
// 1.13e : ??? 
//		maybe structure modification for N64 version 
// 1.13f : 7/12/98 
//		Correction of bug that add Key frame
//		 : 11/01/99
//		Ajout de l'option "d'optimisation" -o 
// 1.13g : 01/02/99
//		Correction for Extra Key frames optimization 
// 1.13h : 16/03/99
//		Correction for vector optimization (fn_v_DiscretizeScal)
// 1.14 : 25/03/99
//		Change treatment of Entry ObjectType in A3D file
//		Check CHL and TBL presence before analysing
//		return 0 if no problem 
//		no wait for keystroke in batch mode
// 1.14a : 30/03/99
//		Correction of event sort
// 1.15 : 16/04/99
//		change the format of ini file to set specific precision
// 1.15a : 16/06/99
//		Correction for deleting hierarchy couple
// 1.16 : 24/06/99
//		Correction precision option bug
//		check STA file with compact option to suppress unused object
// 1.16a : 28/06/99
//		Correction bug analyse STA & unvalidate unused anim
//       : 05/07/99
//		Modif event sort --> use qsort
//		Create Anims dir if not exist
// 1.17 : 20/07/99
//		Modif in quaternion compacting method
// **********************************************************************************

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <sys/stat.h>
#include <direct.h>

#include "afx.h"
#include "SCR.h"

#include "specif\\a3x_pref.h"


#include "a3x_glob.h"
#include "makeanim.h"
#include "a3x_int.h"
#include "a3x_intb.h"
#include "l_global_v6.h"
#include "l_a3d_v6.h"
#include "l_chl_v6.h"
#include "l_Tbl_v6.h"
#include "l_Sta_v6.h"
#include "l_optim_v6.h"
#include "inifile.h"


#define FAMILY_NAME_LENGTH				32

#define NO_PROBLEMO						0
#define ERR_WRONG_PARAMETERS			1
#define ERR_NO_CHL_FILE					2
#define ERR_NO_TBL_FILE					3
#define ERR_NO_STA_FILE					4

// **********************************************************************************
//                                                                Files & Dir Defines
#define VERSION_OPTIONS_FILE			"Version.ini"
#define DIR_SEPARATOR	","

// OPTION command
#define	BATCH_ARG		"b"	// no interaction
#define BATCH_HELP		"\t-b : Batch mode, no interaction\n"

#define	ALL_ARG			"a"	// treat all anim found and save in the same dir
#define ALL_HELP		"\t-a : Treat all anim found and convert in the corresponding Dir\n"

#define	COMPAC_ARG		"z"	// compact Tbl
#define COMPAC_HELP		"\t-z : Save Tbl in Tbo(Original Table) file and compact it\n"

#define	EPSIL_ARG		"p"	// set precision
#define EPSIL_HELP		"\t-p Epsilon : Set the precision for optimisation\n"

#define	OPTIM_ARG		"o"	// pseudo-optimization of non-optim anim
#define OPTIM_HELP		"\t-o KeyFreq : Set the keyFrequence of non-optimized animation(Default=1)\n"

unsigned short	uwNumOfBank=	1;
unsigned short  uwAnalysedAnim;

tdstAnimInfo	stAnims[NombreDanimationsParDirectory];
unsigned short	g_uwTotalNumOfAnim=0;

char			LongActorName[5][500];
char			LongChannelName[500];
char			LongFileName[500],LongFileName2[500];
char			AnimationName[500];

char			szFamilyName[FAMILY_NAME_LENGTH];

int				bCompactTbl=FALSE;

// parametrable variables
SEB_xReal xDefaultEps = (SEB_xReal) 0.0001;
extern int iKeyFreq;

unsigned char bBatch=FALSE;

// ***************************************************************************** Main
// MAIN
// 
// Modif 30/07/98 : Second anim Dir treatment - Carlos Torres
// Modif 17/08/98 : Generalized use of N Dir - Carlos Torres
// Modif 25/08/98 : Add option -a, Let script module found Chl and Tbl File
//					Save file in First dir of version.ini File
// Modif 08/01/99 : Add optim option
// **********************************************************************************
int main(int argc,char * argv[] ) {	
	FILE * fdTest;
	int iCurArg = 1,iFamilyName = -1;
	int bTreatAll=FALSE;
	unsigned short uwNumOfAnim[5];
	unsigned char ucNbDir = 1;
	unsigned short uwNbAnims;
	int i;

	printf("MakeAnim.exe by Sebastien Rubens (Ubi R&D, Montreuil)\nVersion %d.%02d%c\n\n",
		(A3i_Version&0xF000)>>12,(A3i_Version&0x0FF0)>>4,(A3i_Version&0x000F)?'a'+(A3i_Version&0x000F)-1:' ');

	// ------------------------------------------------------------------------------
	// Parameter Treatment
	// ------------------------------------------------------------------------------
	for(iCurArg=1;iCurArg<argc;iCurArg++) {
		// options
		if (*argv[iCurArg]=='-') {
			// batch Option
			if (!stricmp(&argv[iCurArg][1],BATCH_ARG)) {
				bBatch = TRUE;
			}
			// All Option
			else if (!stricmp(&argv[iCurArg][1],ALL_ARG)) {
				bTreatAll = TRUE;
			}
			// Compact Option
			else if (!stricmp(&argv[iCurArg][1],COMPAC_ARG)) {
				bCompactTbl = TRUE;
			}
			// Set precision
			else if (!stricmp(&argv[iCurArg][1],EPSIL_ARG)) {
				if (++iCurArg < argc) {
					sscanf(argv[iCurArg],"%f",&xDefaultEps);
					if ((xDefaultEps < 0) || (xDefaultEps > 0.1)) {
						printf("Bad epsilon specify %f\n",xDefaultEps);
						WaitKeyStroke();
						exit(ERR_WRONG_PARAMETERS);
					}
				}
				else {
					printf("No epsilon specify\n");
					WaitKeyStroke();
					exit(ERR_WRONG_PARAMETERS);
				}
			}
			// Set Key Freq
			else if (!stricmp(&argv[iCurArg][1],OPTIM_ARG)) {
				if (++iCurArg < argc) {
					sscanf(argv[iCurArg],"%d",&iKeyFreq);
					if (iKeyFreq <= 0) {
						printf("Bad KeyFreq specify %d\n",iKeyFreq);
						WaitKeyStroke();
						exit(ERR_WRONG_PARAMETERS);
					}
				}
				else {
					printf("No KeyFreq specify\n");
					WaitKeyStroke();
					exit(ERR_WRONG_PARAMETERS);
				}
			}
			else {
				printf("Option %s Unknown\n",argv[iCurArg]);
				WaitKeyStroke();
				exit(ERR_WRONG_PARAMETERS);
			}
		}

		// Family name Option
		else if (iFamilyName == -1) {
			strcpy(szFamilyName,argv[iCurArg]);
			iFamilyName=iCurArg;
		}

		// Argument unknown
		else {
			printf("Argument %s Unknowm\n",argv[iCurArg]);
			WaitKeyStroke();
			exit(ERR_WRONG_PARAMETERS);
		}
	}

	// read parameter - just the name of character Family
	if (iFamilyName == -1) {	
		printf("Family not specify\n");
		printf("Command format is : MakeAnim [Options] <Family_Name>\n");
		printf("\tGenerate Anim in A3i format in a specific dir(without option -a)\n");
		printf("\tSpecific Dir is the first dir in the %s File\n",VERSION_OPTIONS_FILE);
		printf("Incompatible with the version 1.06 and previous version\n");
		printf("\nOptions :\n");
		printf(BATCH_HELP);
		printf(ALL_HELP);
		printf(COMPAC_HELP);
		printf(OPTIM_HELP);
		WaitKeyStroke();
		exit(ERR_WRONG_PARAMETERS);
	}

	// ------------------------------------------------------------------------------
	// INIT
	// ------------------------------------------------------------------------------
	// init a table with sinus value
	fn_v_InitSinTab();
	// init others modules (ERM MMG SCR)
	fn_v_InitMemV6i();

	fn_v_InitSaveAnimationV6i();

	fn_v_StartA3dV6i();

	// Set channel&CHL path	
	sprintf(LongChannelName,"%s\\%s",DIR_FAMILIES,szFamilyName);
	// Set Anims path
	sprintf(LongActorName[0],"%s\\%s\\%s",DIR_DATA,DIR_ANIMS,szFamilyName);

	// ------------------------------------------------------------------------------
	// Get second Data Dir
	fdTest = fopen(VERSION_OPTIONS_FILE,"rb");
	if (fdTest) {
		char szSecondDataDir[50];
		char szAnimDir[50];
		char * szToken;
		struct _stat stBuf;

		fgets(szSecondDataDir,50,fdTest);
		// suppress last char (EOL)
		szSecondDataDir[(i=strlen(szSecondDataDir))-2] = NULL;
		fclose(fdTest);

		// Look for a third Dir
		szToken = strtok(szSecondDataDir,DIR_SEPARATOR);

		// check Anims dir for specif gamedata
		sprintf(szAnimDir,"%s\\%s",szToken,DIR_ANIMS,szFamilyName);
		// check if Dir Exist & create it if necessary
		if (_stat(szAnimDir,&stBuf))
			_mkdir(szAnimDir);

		// Get all dirs
		while (szToken) {
			// Set Anims path
			sprintf(LongActorName[ucNbDir],"%s\\%s\\%s",szToken,DIR_ANIMS,szFamilyName);
			// check if Dir Exist
			if (!_stat(LongActorName[ucNbDir],&stBuf))
					ucNbDir++;
			// if not all option create Specific Dir
			else if (!bTreatAll && (ucNbDir == 1)) {
				// create Save Dir
				_mkdir(LongActorName[ucNbDir]);
				ucNbDir++;
			}

			szToken = strtok(NULL,DIR_SEPARATOR);
		}
	}

	// Get anim number in each Directory for the family 
	for (i=0;i<ucNbDir;i++) {
		uwNumOfAnim[i] = fn_uw_GetDirV6i(LongActorName[i],bTreatAll);
	}

	// ------------------------------------------------------------------------------
	// LOAD MakeAnim.ini File
	// ------------------------------------------------------------------------------
	fn_v_IniFileInit(szFamilyName);
	sprintf(LongFileName,"%s^%s",MAKEANIM_OPTIONS_FILE,szFamilyName);

	// check if file exist
	if (SCR_fn_c_RdL0_IsSectionExists(LongFileName)) {
		SCR_fnp_st_RdL0_AnalyseSection( LongFileName, SCR_CDF_uw_Anl_NotSaveSection );
		SCR_fn_v_RdL0_Close();
	}

	// ------------------------------------------------------------------------------
	// LOAD CHL File
	// ------------------------------------------------------------------------------
	fn_v_InitCHLV6i();

	sprintf(LongFileName,"%s\\%s.CHL",LongChannelName,szFamilyName);

	// check if file exist
	if (SCR_fn_c_RdL0_IsSectionExists(LongFileName)) {
		SCR_fnp_st_RdL0_AnalyseSection( LongFileName, SCR_CDF_uw_Anl_NotSaveSection );
		SCR_fn_v_RdL0_Close();
	}
	else {
		printf("File %s not found\n",LongFileName);
		WaitKeyStroke();
		return ERR_NO_CHL_FILE;
	}

	// ------------------------------------------------------------------------------
	// Copy Originals Table in Tbl File or save original TBL
	// ------------------------------------------------------------------------------
	if (bCompactTbl) {
		struct _finddata_t stFileInfo;
		long lHandle;

		// Find Tbl in data
		for (i=0;i<ucNbDir;i++) {
			strcpy(LongFileName,LongActorName[i]);
			sprintf(strchr(LongFileName,'\\') + 1,"%s\\%s\\*.TBL",REL_DIR_FAMILY,szFamilyName);
			lHandle = _findfirst(LongFileName,&stFileInfo);

			// stop at first TBL found
			if (lHandle != -1)
				break;
		}

		// compact all family's tbl
		if (lHandle != -1) {
			do {
				// Build File name
				sprintf(strrchr(LongFileName,'\\')+1,"%s",stFileInfo.name);
				strcpy(LongFileName2,LongFileName);
				LongFileName2[strlen(LongFileName2)-1] = 'O';

				// Get tbl original
				if (!CopyFile(LongFileName2,LongFileName,NULL))
					// save original tbl
					CopyFile(LongFileName,LongFileName2,NULL);
			} while (!_findnext(lHandle,&stFileInfo));

			_findclose(lHandle);
		}
	}


	// ------------------------------------------------------------------------------
	// LOAD TBL File
	// ------------------------------------------------------------------------------
	fn_v_InitTBLV6i();

	sprintf(LongFileName,"%s\\%s.TBL",LongChannelName,szFamilyName);

	// check if file exist
	if (SCR_fn_c_RdL0_IsSectionExists(LongFileName)) {
		SCR_fnp_st_RdL0_AnalyseSection( LongFileName, SCR_CDF_uw_Anl_NotSaveSection );
		SCR_fn_v_RdL0_Close();
	}
	else {
		printf("File %s not found\n",LongFileName);
		WaitKeyStroke();
		return ERR_NO_TBL_FILE;
	}

	// ------------------------------------------------------------------------------
	// Check State file
	// ------------------------------------------------------------------------------
	if (bCompactTbl) {
		fn_v_InitSTAV6i();
		sprintf(LongFileName,"%s\\%s.STA",LongChannelName,szFamilyName);

		// check if file exist
		if (SCR_fn_c_RdL0_IsSectionExists(LongFileName)) {
			SCR_fnp_st_RdL0_AnalyseSection( LongFileName, SCR_CDF_uw_Anl_NotSaveSection );
			SCR_fn_v_RdL0_Close();
		}
		else {
			printf("File %s not found\n",LongFileName);
			WaitKeyStroke();
			return ERR_NO_STA_FILE;
		}
	}

	// ------------------------------------------------------------------------------
	// LOAD A3D Files (all Family's animation)
	// ------------------------------------------------------------------------------
	fn_v_InitA3dV6i();
	for	( uwAnalysedAnim=0 ; uwAnalysedAnim<g_uwTotalNumOfAnim; uwAnalysedAnim++ ) {
		if (!bValid(&stAnims[uwAnalysedAnim])) {
			// Set AnimationName
			sprintf(AnimationName,"%s\\%s",szFamilyName,szAnimName(&stAnims[uwAnalysedAnim]));

			// search the good dir
			for (i=0,uwNbAnims=uwNumOfAnim[0];i<ucNbDir;i++,uwNbAnims+=uwNumOfAnim[i])
				if (uwAnalysedAnim < uwNbAnims)
					break;

			// Set LongFileName
			sprintf(LongFileName,"%s\\%s",LongActorName[i],szAnimName(&stAnims[uwAnalysedAnim]));

			SCR_fnp_st_RdL0_AnalyseSection( LongFileName, SCR_CDF_uw_Anl_NotSaveSection );
			fn_v_KillMem();
		}
		else {
			// always allocate A3D General in stack to respect indexing
			fn_v_AllocateOnStack( eStackBinA3dGENERAL, 1 );
		}
	}

	// ------------------------------------------------------------------------------
	// BINARISE ANIM
	// ------------------------------------------------------------------------------
	fn_v_VerifyAnimationV6i(g_uwTotalNumOfAnim);
	fn_v_OptimiseAnimationV6i(g_uwTotalNumOfAnim);
	fn_v_OptimiseAnimation2V6i(g_uwTotalNumOfAnim);
	
	// ------------------------------------------------------------------------------
	// UPDATE TBL 
	// ------------------------------------------------------------------------------
	if (bCompactTbl) {
		struct _finddata_t stFileInfo;
		long lHandle;

		// Find Tbl in data
		for (i=0;i<ucNbDir;i++) {
			strcpy(LongFileName,LongActorName[i]);
			sprintf(strchr(LongFileName,'\\') + 1,"%s\\%s\\*.TBL",REL_DIR_FAMILY,szFamilyName);
			lHandle = _findfirst(LongFileName,&stFileInfo);

			// stop at first TBL found
			if (lHandle != -1)
				break;
		}

		// compact all family's tbl
		if (lHandle != -1) {
			fn_vGenerateNewObjectNumber(TRUE);

			do {
				// Build File name
				sprintf(strrchr(LongFileName,'\\')+1,"%s",stFileInfo.name);
				
				// update TBL to compact it
				fn_vCompactTBL(LongFileName);
			} while (!_findnext(lHandle,&stFileInfo));

			_findclose(lHandle);
		}
	}
	// Keep tbl index for uncompactable TBL
	else
		fn_vGenerateNewObjectNumber(FALSE);


	// ------------------------------------------------------------------------------
	// SAVE ANIM
	// ------------------------------------------------------------------------------
	for (i=0,uwNbAnims=0;i<ucNbDir;i++) {
		if (bTreatAll || (ucNbDir==1))
			fn_v_SaveAnimationV6i(uwNbAnims,uwNumOfAnim[i],LongActorName[i]);
		else
			fn_v_SaveAnimationV6i(uwNbAnims,uwNumOfAnim[i],LongActorName[1]);
		uwNbAnims += uwNumOfAnim[i];
	}
	//TagadaPouetPouet( uwNumOfAnim );

	// ------------------------------------------------------------------------------
	// END OF PROGRAM
	// ------------------------------------------------------------------------------
	// verify if stacks is enough big
	fn_vCheckStacks();
	
	fn_v_EndA3dV6i();
	SCR_fn_v_RdL0_Close();

	fn_v_EndSaveAnimationV6i();

	printf( "\n\nFin de binarisation...\n" );

	return 0;
}



// **********************************************************************************
#undef MAKEANIM_C
