#include "ACP_Base.h"
#include "ToolsCPA.h"

#include "mmg.h"
#include "SNA.h"
#include "acp_opfi.h"
#include "SNAFile.h"


/* ANNECY OA - 13/07/99 { */
#define C_MAX_CONNECTED_FILES	20
char *a_BigFilesName[C_MAX_CONNECTED_FILES] = {
		"LEVELS.DAT", 
		"MAP.DAT", 
		"TEXTURES.DAT", 
		"ANIMS.DAT",
		"DATA01.DAT",
		"DATA02.DAT",
		"DATA03.DAT",
		"DATA04.DAT",
		"DATA05.DAT",
		"DATA06.DAT"
		"DATA07.DAT",
		"DATA08.DAT",
		"DATA09.DAT",
		"DATA10.DAT",
		"DATA11.DAT",
		"DATA12.DAT",
		"DATA13.DAT",
		"DATA14.DAT",
		"DATA15.DAT",
		"DATA16.DAT",
};
/* END ANNECY OA } */



/*------------------------------------------------------------------------*/
#define SNA_M_OffSet_Error		0xFFFFFFFF
/*------------------------------------------------------------------------*/

unsigned char SNA_g_ucNextRelocationTableToLoad[4];
tdstRelocTable *SNA_g_PTCRelocationTable = NULL;

DWORD	SNA_g_a_dwInitKeyRelocationTableLoaded[SNA_M_ProtectRelocationTableSize];
DWORD	SNA_g_a_dwProtectKeyRelocationTableLoaded[SNA_M_ProtectRelocationTableSize];
DWORD	SNA_g_dwNumberOfRelocationTableLoaded;

/*
 *
 *
 */
void SNA_fn_vInitBigFile();
ACP_tdxBool SNA_fn_bGetHandleToRelocationTableInBigFile( SNA_tdstFile *_p_stFile );

/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
HANDLE SNA_fn_hGetOffSetInBigFileWithSinusHeader(DWORD *p_dwOffSet);

#define M_SECTOR_SIZE	2048

DWORD SNA_g_dwNumberOfOccur;
DWORD SNA_g_dwFirstHeaderSize;
DWORD SNA_g_dwHeaderSize;
DWORD SNA_g_dwTotalSector;
DWORD SNA_g_dwConnectedFiles;

/*------------------------------------------------------------------------*/
#pragma warning( disable : 4102 )  // Disable warning messages : unreferenced label

/*
 * Function to convert a relocation table file number + file type to a real file handle.
 * This is version without protection bigfile.
 */
#include "GAM\structur\engmode.h"

ACP_tdxBool SNA_fn_bGetHandleToRelocationTable( SNA_tdstFile *_p_stFile )
{
	char szFileName[256];
	char *p_szLevelName, *p_cSubMapDelimiter;


	/* ------------------------------------------ */
	/* to get begin and end addresses of function */
#ifdef PTC_SYSTEM_ACTIVED
	if (PTC_g_bTakeAddresses)
	{
		PTC_M_AddFunctionCheckSum(0,SNA_fn_bGetHandleToRelocationTable);
		return TRUE;
	}
#endif
	/* ------------------------------------------ */
	/* ------------------------------------------ */


PTC_M_DeclareBeginLabel(SNA_fn_bGetHandleToRelocationTable)

	/* Loading fix ? */
	if( SNA_g_ucNextRelocationTableToLoad[0] == 255 )
	{
/* ANNECY OA - 20/07/99 { */
#ifdef RETAIL
		sprintf( szFileName, "%c:\\%s\\Fix", g_cCDROM, fn_szGetLevelsDataPath() );		
#else
		sprintf( szFileName, "%s\\Fix", fn_szGetLevelsDataPath() );		
#endif
/* END ANNECY OA } */
	}
	else
	{
#ifdef DEBUG
		/* Trying to load a level that does not exist ? */
		assert( SNA_g_ucNextRelocationTableToLoad[0] < g_stEngineStructure.ucNumberOfLevels );
#endif

/* ANNECY MT - 30/03/99 { Pc Protection */
		/* Trying to load rt? file into BigFile */
		if (SNA_fn_bBigFileUsed ())
		{
			if (SNA_fn_bGetHandleToRelocationTableInBigFile (_p_stFile))
				return TRUE;
		}
/* END ANNECY MT } */

		/* Get level name from its number */
		p_szLevelName = g_stEngineStructure.a_szLevelName[SNA_g_ucNextRelocationTableToLoad[0]];

		if( (p_cSubMapDelimiter = strchr( p_szLevelName, '$' )) != NULL )
			*p_cSubMapDelimiter = 0;

		/* Compute full level name (with path) */
/* ANNECY OA - 20/07/99 { */
#ifdef RETAIL
		sprintf( szFileName, "%c:\\%s\\%s\\%s", g_cCDROM, fn_szGetLevelsDataPath(), p_szLevelName, p_szLevelName );
#else
		sprintf( szFileName, "%s\\%s\\%s", fn_szGetLevelsDataPath(), p_szLevelName, p_szLevelName );
#endif
/* END ANNECY OA } */

		if( p_cSubMapDelimiter )
			*p_cSubMapDelimiter = '$';
	}

	/* add file extension (given its type) */
	switch( SNA_g_ucNextRelocationTableToLoad[1] )
	{
		case SNA_C_ucSNARelocationTable:             strcat( szFileName, ".rtb" ); break;
		case SNA_C_ucGlobalPointersRelocationTable:  strcat( szFileName, ".rtp" ); break;
		case SNA_C_ucTexturesRelocationTable:        strcat( szFileName, ".rtt" ); break;
		case SNA_C_ucSoundRelocationTable:           strcat( szFileName, ".rts" ); break;
		case SNA_C_ucLipsSyncRelocationTable:        strcat( szFileName, ".rtl" ); break;
#ifdef DEBUG
		/* Unknown relocation file type */
		default: assert(0);
#endif
	}


PTC_M_DeclareEndLabel(SNA_fn_bGetHandleToRelocationTable)

	/* Open it and returns handle. */
/* ANNECY OA - 20/07/99 { */
	{
		BOOL returnValue = SNA_fn_bFOpen( szFileName, SNA_C_ucRead, 0, _p_stFile );
#ifdef RETAIL
		while (returnValue == FALSE)
		{
			if (MessageBox(NULL, "Inserez le CD dans le lecteur", "CDROM non trouvé!", MB_RETRYCANCEL) == IDCANCEL)
			{
				abort();
			}
			returnValue = SNA_fn_bFOpen( szFileName, SNA_C_ucRead, 0, _p_stFile );
		}
#endif		
		return returnValue;
		/*return SNA_fn_bFOpen( szFileName, SNA_C_ucRead, 0, _p_stFile );*/
	}
/* END ANNECY OA } */
}

/* Return the position of a level name in global array of level names. */
unsigned char SNA_fn_ucGetLevelID( char *_p_szLevelName )
{
	unsigned int i = g_stEngineStructure.ucNumberOfLevels;
	unsigned iLength = strlen( _p_szLevelName );
	while( i-- )
	{
		if( strnicmp( _p_szLevelName, g_stEngineStructure.a_szLevelName[i], iLength ) == 0 )
			return i;
	}
#ifndef RETAIL
	// We did not find the level in list of level => the program will crash. so....
	{
		FILE *hFile = fopen( "test.log", "at" );
		if( hFile )
		{
			fprintf( hFile, "Can't change to map '%s': not found in map list\n", _p_szLevelName );
			fclose( hFile );
			exit( 1 );
		}
	}
	assert(0);
#endif
	return (unsigned char)-1;
}

/**
 * SNA_fn_pLoadRelocationTable :
 * Read one .rt? file containing a relocation table.
 */
tdstRelocTable *SNA_fn_pLoadRelocationTable()
{
	SNA_tdstFile stFile;
	unsigned char ucModule=0xf0,ucBloc=0xf0,ucNbOfBloc,i,ucOldModule=0xf0,ucOldBloc=0xf0;
	unsigned long ulSize/*,j*/;
	tdstRelocTable *p_stRelocTable;
	tdstBloc *p_stBloc;
	tdstPtr *p_stPtr;

	SNA_fn_vInitArrays();

	if( ! SNA_fn_bGetHandleToRelocationTable( &stFile ) )
		return 0;

	SNA_fn_ulFRead( &ucNbOfBloc , sizeof(char), 1, &stFile );

	p_stRelocTable=(tdstRelocTable *)malloc(sizeof(tdstRelocTable));
	p_stRelocTable->ucBlocNumber=ucNbOfBloc;

	p_stBloc=(tdstBloc *)malloc(sizeof(tdstBloc)*ucNbOfBloc);
	p_stRelocTable->p_stBloc=p_stBloc;

	for(i=0;i<ucNbOfBloc;i++)
	{
		ucOldModule=ucModule;
		ucOldBloc=ucBloc;

		SNA_fn_ulFRead( &ucModule , sizeof(char), 1, &stFile );

		SNA_fn_ulFRead( &ucBloc , sizeof(char), 1, &stFile );
		if( (ucModule==ucOldModule) && (ucBloc==ucOldBloc) )     // MR0610 (anti-bug)
		{
			p_stRelocTable->ucBlocNumber--;
			break;
		}

		SNA_fn_ulFRead( &ulSize , sizeof(long), 1, &stFile );

		if( ulSize != 0L )
		{
			p_stPtr=(tdstPtr *)malloc(sizeof(tdstPtr)*ulSize);
			/*
			for(j=0;j<ulSize;j++)
			{
				SNA_fn_ulFRead( &p_stPtr[j].p_vPtr , sizeof(long), 1, &stFile );
				SNA_fn_ulFRead( &p_stPtr[j].ucTargetModule , sizeof(char), 1, &stFile );
				SNA_fn_ulFRead( &p_stPtr[j].ucTargetBlock , sizeof(char), 1, &stFile );
			}
			*/
			SNA_fn_ulFRead( p_stPtr , sizeof(tdstPtr) * ulSize , 1, &stFile );
		}
		else
		{
			p_stPtr=0;
		}
		p_stBloc[i].ucModuleNumber=ucModule;
		p_stBloc[i].ucBlocNumber=ucBloc;
		p_stBloc[i].ulSize=ulSize;
		p_stBloc[i].p_stPtr=p_stPtr;
	}

	SNA_fn_bFClose( &stFile );

	return p_stRelocTable;
}

void SNA_fn_pFreeRelocationTable(tdstRelocTable * p_stRelocTable)
{
	unsigned char i;
	tdstBloc *p_stBloc;

	if(p_stRelocTable!=NULL)
	{
		p_stBloc=p_stRelocTable->p_stBloc;
		for(i=0;i<p_stRelocTable->ucBlocNumber;i++)
		{
			free(p_stBloc[i].p_stPtr);
		}
		free(p_stBloc);
		free(p_stRelocTable);
	}
}

/* ANNECY MT - 30/03/99 { PC Protection */


/*------------------------------------------------------------------------
 *
 * Author			Marc Trabucato
 * Date				31 mars 1999
 *
 * Prototype		SNA_fn_vInitBigFile
 * Return Type		void
 *
 * Description
 *
 *------------------------------------------------------------------------*/


void SNA_fn_vInitBigFile()
{
	if(SNA_fn_bBigFileUsed ())
	{
		char szBigFileName[MAX_PATH];
		HANDLE hFile;
		
		/* ANNECY OA - 13/07/99 { */
#ifdef RETAIL
		sprintf (szBigFileName , "%c:\\%s\\%s", g_cCDROM, fn_szGetLevelsDataPath(), a_BigFilesName[0]);
#else
		sprintf (szBigFileName , "%s\\%s", fn_szGetLevelsDataPath(), a_BigFilesName[0]);
#endif
		/*sprintf (szBigFileName , "%s\\data.000", fn_szGetLevelsDataPath());*/
		/* END ANNECY OA } */
		
		hFile = PTC_CreateFile (szBigFileName , GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL);

/* ANNECY OA - 20/07/99 { */
#ifdef RETAIL
		while (hFile == INVALID_HANDLE_VALUE)
		{
			if (MessageBox(NULL, "Inserez le CD dans le lecteur", "CDROM non trouvé!", MB_RETRYCANCEL) == IDCANCEL)
			{
				abort();
			}
			hFile = PTC_CreateFile (szBigFileName , GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL);
		}
#endif
/* END ANNECY OA } */
				
		if (hFile != INVALID_HANDLE_VALUE)
		{
			DWORD dwKeys[4];
			DWORD dwRead;
			DWORD dwValue , dwUnused;
			
			/* read keys */
			PTC_ReadFile (hFile , dwKeys , 4 * sizeof(DWORD) , &dwRead , NULL);
			
			/* read Number of map */
			PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
			dwUnused = (dwValue - dwKeys[0]) ^ dwKeys[1];
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
			
			/* read Number of Occur */
			PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
			SNA_g_dwNumberOfOccur = (dwValue - dwKeys[0]) ^ dwKeys[1];
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
			
			/* read Header Step */
			PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
			dwUnused = (dwValue - dwKeys[0]) ^ dwKeys[1];
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
			
			/* read First Header Size */
			PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
			SNA_g_dwFirstHeaderSize = (dwValue - dwKeys[0]) ^ dwKeys[1];
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
			
			/* read File Step */
			PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
			dwUnused = (dwValue - dwKeys[0]) ^ dwKeys[1];
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
			
			/* read Sector Size */
			PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
			SNA_g_dwHeaderSize = (dwValue - dwKeys[0]) ^ dwKeys[1];
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
			
			/* read Number of Sectors */
			PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
			SNA_g_dwTotalSector = (dwValue - dwKeys[0]) ^ dwKeys[1];
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
			
			/* read Number of Connected Files */
			PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
			SNA_g_dwConnectedFiles = (dwValue - dwKeys[0]) ^ dwKeys[1];
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
			
			PTC_CloseHandle (hFile);
		}
	}
}



/*------------------------------------------------------------------------
 *
 * Author			Marc Trabucato
 * Date				31 mars 1999
 *
 * Prototype		SNA_fn_bGetHandleToRelocationTableInBigFile
 * Parameters		_p_stFile :
 * Return Type		ACP_tdxBool
 *
 * Description		Read rt? files into bigfile for each map
 *
 *------------------------------------------------------------------------*/
ACP_tdxBool SNA_fn_bGetHandleToRelocationTableInBigFile( SNA_tdstFile *_p_stFile )
{
	DWORD dwOffSet;
	DWORD dwProtectKey;
	HANDLE hFile;

	/* ------------------------------------------ */
	/* to get begin and end addresses of function */
#ifdef PTC_SYSTEM_ACTIVED
	if (PTC_g_bTakeAddresses)
	{
		PTC_M_AddFunctionCheckSum(1,SNA_fn_bGetHandleToRelocationTableInBigFile);
		return TRUE;
	}
#endif
	/* ------------------------------------------ */
	/* ------------------------------------------ */


PTC_M_DeclareBeginLabel(SNA_fn_bGetHandleToRelocationTableInBigFile)

	hFile = SNA_fn_hGetOffSetInBigFileWithSinusHeader (&dwOffSet);

	if (hFile != INVALID_HANDLE_VALUE)
	{
		/* seek */
		PTC_SetFilePointer (hFile , dwOffSet , NULL , FILE_BEGIN);

		/* uses encryption */
		SNA_fn_vInitFile (SNA_C_ucUseEncryption , _p_stFile);

		// compute crypt key */*
		SNA_g_ucNextRelocationTableToLoad[3] = ~SNA_g_ucNextRelocationTableToLoad[2];

		_p_stFile -> ulCryptKey = SNA_fn_dwGetCryptKeyRelocationTable();

		SNA_g_a_dwInitKeyRelocationTableLoaded [SNA_g_dwNumberOfRelocationTableLoaded] = *((DWORD*)SNA_g_ucNextRelocationTableToLoad);

		_p_stFile -> hFile = hFile;

		_p_stFile -> bReadSuccess = TRUE;

		/* read protect-key */
		SNA_fn_ulFRead (&dwProtectKey , 4 , 1 , _p_stFile);

		SNA_g_a_dwProtectKeyRelocationTableLoaded [SNA_g_dwNumberOfRelocationTableLoaded++] = dwProtectKey;

		SNA_M_vUpdateOccurrenceToLoad();

		return TRUE;
	}

PTC_M_DeclareEndLabel(SNA_fn_bGetHandleToRelocationTableInBigFile)

	return FALSE;
}



/*------------------------------------------------------------------------
 *
 * Author			Marc Trabucato
 * Date				31 mars 1999
 *
 * Prototype		SNA_fn_dwGetOffSetInBigFileWithSinusHeader
 * Return Type		DWORD
 *
 * Description		each occurence has it's own header in the file
 *					Headers are placed int the file using a recurent serie
 *------------------------------------------------------------------------*/
HANDLE SNA_fn_hGetOffSetInBigFileWithSinusHeader(DWORD *p_dwOffSet)
{
	char szBigFileName[MAX_PATH];
	HANDLE hFile;

/* ANNECY OA - 13/07/99 { */
#ifdef RETAIL
	sprintf (szBigFileName , "%c:\\%s\\%s", g_cCDROM, fn_szGetLevelsDataPath(), a_BigFilesName[0]);
#else
	sprintf (szBigFileName , "%s\\%s", fn_szGetLevelsDataPath(), a_BigFilesName[0]);
#endif
	/*sprintf (szBigFileName , "%s\\data.000", fn_szGetLevelsDataPath());*/
/* END ANNECY OA } */

	hFile = PTC_CreateFile (szBigFileName , GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL);

/* ANNECY OA - 20/07/99 { */
#ifdef RETAIL
	while (hFile == INVALID_HANDLE_VALUE)
	{
		if (MessageBox(NULL, "Inserez le CD dans le lecteur", "CDROM non trouvé!", MB_RETRYCANCEL) == IDCANCEL)
		{
			abort();
		}
		hFile = PTC_CreateFile (szBigFileName , GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL);
	}
#endif
/* END ANNECY OA } */

	if (hFile != INVALID_HANDLE_VALUE)
	{
		DWORD dwFile , dwOccur , dwOffSetHeader;
		DWORD dwKeys[4];
		DWORD dwRead;
		DWORD dwValue;
		DWORD dwCount;
		DWORD dwN;
		double fN , fValue , fA , fB , fC, fInteger;

		// Compute Occur and File
		dwFile = SNA_g_ucNextRelocationTableToLoad[0] * 4;
		switch( SNA_g_ucNextRelocationTableToLoad[1] )
		{
			case SNA_C_ucSNARelocationTable:							break;
			case SNA_C_ucGlobalPointersRelocationTable:  dwFile += 1;	break;
			case SNA_C_ucSoundRelocationTable:           dwFile += 2;	break;
			case SNA_C_ucTexturesRelocationTable:        dwFile += 3;	break;
#ifdef DEBUG
			case SNA_C_ucLipsSyncRelocationTable:
			default: assert(0);
#endif
		}
		dwOccur = (SNA_g_ucNextRelocationTableToLoad[2]) % SNA_g_dwNumberOfOccur;

		SNA_g_ucNextRelocationTableToLoad[2] = (unsigned char) (dwOccur & 0x000000FF);

		// seek wanted header
		fA = 0.69314;
		fB = 1.69314;
		fC = 0.52658;
		fN = 1.06913;

		for (dwN = 0 ; dwN < dwOccur ; dwN++)
		{
			fN = fN + fA * fabs(sin(fB * dwN * dwN)) + fC;
			//fN = floor(fN * 1000000.0) / 1000000.0;
		}
		fValue = floor(modf(fN , &fInteger) * 1000000.0) / 1000000.0;
		dwOffSetHeader = SNA_g_dwHeaderSize * (DWORD)floor(fValue * SNA_g_dwTotalSector) + SNA_g_dwFirstHeaderSize;
		PTC_SetFilePointer (hFile , dwOffSetHeader , NULL , FILE_BEGIN);

		// read crypt key
		PTC_ReadFile (hFile , dwKeys , 4 * sizeof(DWORD) , &dwRead , NULL);

		// seek wanted file
		for (dwCount = 0 ; dwCount < dwFile ; dwCount++)
		{
			dwKeys[0] += dwKeys[2];
			dwKeys[1] += dwKeys[3];
		}

		PTC_SetFilePointer (hFile , dwFile * 4 , NULL , FILE_CURRENT);

		/* read OffSet */
		PTC_ReadFile (hFile , &dwValue , 4 , &dwRead , NULL);
		*p_dwOffSet = (dwValue - dwKeys[0]) ^ dwKeys[1];

/* ANNECY OA - 15/07/99 { */
		/* Multi files managment */
		{
			char sBuffer[512];
			char nbuffer[512];
			
			long lSectorOffset = SNA_g_dwTotalSector / SNA_g_dwConnectedFiles;
			long lByteOffset = lSectorOffset*M_SECTOR_SIZE;
			long lHigherFileNumber = *p_dwOffSet / lByteOffset;
			long lFileToOpen = PTC_fn_lGetRandomRange(0, lHigherFileNumber);

			PTC_SetFilePointer (hFile , *p_dwOffSet , NULL , FILE_BEGIN);
			PTC_ReadFile (hFile , sBuffer , 512 * sizeof(char) , &dwRead , NULL);			
						
			*p_dwOffSet -= lFileToOpen*lByteOffset;

			PTC_CloseHandle(hFile);

/* ANNECY OA - 20/07/99 { */
#ifdef RETAIL
			sprintf (szBigFileName , "%c:\\%s\\%s", g_cCDROM, fn_szGetLevelsDataPath(), a_BigFilesName[lFileToOpen]);
#else
			sprintf (szBigFileName , "%s\\%s", fn_szGetLevelsDataPath(), a_BigFilesName[lFileToOpen]);
#endif
/* END ANNECY OA } */
			hFile = PTC_CreateFile (szBigFileName , GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL);

/* ANNECY OA - 20/07/99 { */
#ifdef RETAIL
			while (hFile == INVALID_HANDLE_VALUE)
			{
				if (MessageBox(NULL, "Inserez le CD dans le lecteur", "CDROM non trouvé!", MB_RETRYCANCEL) == IDCANCEL)
				{
					abort();
				}
				hFile = PTC_CreateFile (szBigFileName , GENERIC_READ , 0 , NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL);
			}
#endif
/* END ANNECY OA } */

			PTC_SetFilePointer (hFile , *p_dwOffSet , NULL , FILE_BEGIN);
			PTC_ReadFile (hFile , nbuffer , 512 * sizeof(char) , &dwRead , NULL);
		}
/* END ANNECY OA } */
	}

	return hFile;
}

/* END ANNECY MT } */

/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
