/*=========================================================================
* Snammg.c : Save & Read the memory blocks
*
* Version              1.0
* Creation date        03/09/97
* Revision date
*
* (c) Ubi R&D 1997
*=======================================================================*/

#include "MMG.h"
#include "ToolsCPA.h"
#include "SNA.h"
#include "snafile.h"
#include "windows.h"
#include "acp_opfi.h"


// This define allows the encryption of the .sna files.
// Comment it for no encryption.
#define SNA_ENCRYPTION

ACP_tdxBool g_a_bIsBlocRelocated[C_ucNbOfMaxModule][10];
long g_a_ulOffset[C_ucNbOfMaxModule][10];

extern unsigned char g_ucGameModuleId;
extern unsigned char g_ucAIModuleId;
extern unsigned char g_ucGEOModuleId;
extern unsigned char g_ucIPTModuleId;
extern unsigned char g_ucFONModuleId;
extern unsigned char g_ucTMPModuleId;
extern unsigned char g_ucSndModuleId;

// Memory blocks that are saved for each level.
// Ex: (Game,0) => Block 0 of module Game.
struct tdstModuleBloc_ g_a_stModuleBlocToSaveInLevel[] =
{
	SNA_M_SetBlocForSaving( Game, 1 ),
	SNA_M_SetBlocForSaving( GEO, 2 ),
	SNA_M_SetBlocForSaving( AI, 1 )
};

// Memory blocks that are saved in the fix.
struct tdstModuleBloc_ g_a_stModuleBlocToSaveInFix[] =
{
	SNA_M_SetBlocForSaving( Game, 0 ),
	SNA_M_SetBlocForSaving( GEO, 0 ),
	SNA_M_SetBlocForSaving( GEO, 1 ),
	SNA_M_SetBlocForSaving( IPT, 0 ),
	SNA_M_SetBlocForSaving( AI, 0 ),
	SNA_M_SetBlocForSaving( FON, 0 )
};

// Memory blocks that are NOT saved, but some infos will be included for relocation purpose.
// Actually, pointers from other blocks to these blocks will be correctly relocated.
// (except for TMP block: pointers to TMP will be set to NULL at load time)
struct tdstModuleBloc_ g_a_stModuleBlocToSaveInfos[] =
{
	SNA_M_SetBlocForSaving( TMP, 0 ),
	SNA_M_SetBlocForSaving( Snd, 0 )
};

unsigned long SNA_g_ulNbBlocksInFix = SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInFix);
unsigned long SNA_g_ulNbBlocksInLevel = SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInLevel);

char g_bSaveLoadFix;


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// Sort an array of blocks so that blocks are sorted by module and block number.
// An old good bubble sort :-)
void SNA_fn_vSortAnArray( struct tdstModuleBloc_ *_p_stArray, unsigned long _ulArraySize )
{
	struct tdstModuleBloc_ *p_stArray2;
	struct tdstModuleBloc_ *p_stLastElement = _p_stArray + _ulArraySize-1;
	struct tdstModuleBloc_ *p_stEndOfArray = p_stLastElement + 1;
	struct tdstModuleBloc_  stTmp;

	for( ; _p_stArray < p_stLastElement; _p_stArray++ )
		for( p_stArray2 = _p_stArray+1; p_stArray2 < p_stEndOfArray; p_stArray2++ )
			if( (*(_p_stArray->p_ucModuleId) > *(p_stArray2->p_ucModuleId))
			    || ( (*(_p_stArray->p_ucModuleId) == *(p_stArray2->p_ucModuleId))
			         && (_p_stArray->ucBlockId > p_stArray2->ucBlockId) ) )
			{
				memcpy( &stTmp, _p_stArray, sizeof(struct tdstModuleBloc_) );
				memcpy( _p_stArray, p_stArray2, sizeof(struct tdstModuleBloc_) );
				memcpy( p_stArray2, &stTmp, sizeof(struct tdstModuleBloc_) );
			}
}

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
void SNA_fn_vSetSaveLoadFix()
{
	g_bSaveLoadFix=TRUE;
}

void SNA_fn_vSetSaveLoadLevel()
{
	g_bSaveLoadFix=FALSE;
}


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// Tells if the bloc is to be loaded.
// Assumes that arrays of block are SORTED by module and block.
BOOL SNA_fn_bLoadThisModuleBloc( unsigned char _ucModule, unsigned char _ucBlock )
{
	struct tdstModuleBloc_ *p_stArray, *p_stEndArray;
	// Test Fix array when we load the fix or level array when we load the level
	if( g_bSaveLoadFix )
	{
		p_stArray = g_a_stModuleBlocToSaveInFix;
		p_stEndArray = p_stArray+SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInFix);
	}
	else
	{
		p_stArray = g_a_stModuleBlocToSaveInLevel;
		p_stEndArray = p_stArray+SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInLevel);
	}

	// Go to the first block that have the module number "_ucModule"
	while(   p_stArray < p_stEndArray
	      && _ucModule > *(p_stArray->p_ucModuleId) )
		p_stArray ++;

	// If we find one block with the good module ID, find the one with the good block ID.
	if( p_stArray<p_stEndArray )
		while(   p_stArray < p_stEndArray
		      && _ucModule == *(p_stArray->p_ucModuleId) )
		{
			if( _ucBlock == p_stArray->ucBlockId )
				// Found one, return TRUE
				return TRUE;
			p_stArray ++;
		}

	// Block was not found in array, return FALSE
	return FALSE;
}

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// Initialise the global arrays that hold the block to save and load.
// Basically, that means sorting them by module ID and block ID.
// The sorting needs to be done only once.
void SNA_fn_vInitArrays()
{
	static ucArrayAreSorted = 0;
	if( ! ucArrayAreSorted )
	{
		SNA_fn_vSortAnArray(g_a_stModuleBlocToSaveInFix, SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInFix) );
		SNA_fn_vSortAnArray(g_a_stModuleBlocToSaveInLevel, SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInLevel) );
		SNA_fn_vSortAnArray(g_a_stModuleBlocToSaveInfos, SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInfos) );
		ucArrayAreSorted = 1;
	}
}

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// Write the MMG header of each block in the given array to file.
//
// Input:
//     _p_stArrayToSaveInfos : an array of block (ie: g_a_stModuleBlocToSaveInFix, etc)
//     _ulNbElem             : Number of elements in the array
//     _ucBlockType          : Type of block in the array (SNA_C_ucBlockOfFix, SNA_C_ucBlockOfLevel or SNA_C_ucEmptyBlock)
//     _p_stFile             : An opened file to write the infos.
void SNA_fn_vWriteMemoryInfosOfArray(
    struct tdstModuleBloc_ *_p_stArrayToSaveInfos, unsigned long _ulNbElem,
    unsigned char _ucBlocType, struct SNA_tdstFile_ *_p_stFile )
{
	unsigned char ucModule, ucBlock;
	unsigned long ulBlockSize,ulBeginBlock,ulEndBlock,ulMaxMem;
	tdstBlockInfo stBloc;

	while( _ulNbElem-- )
	{
		ucModule = *(_p_stArrayToSaveInfos->p_ucModuleId);
		ucBlock = _p_stArrayToSaveInfos->ucBlockId;

		// Write block header :  ModuleID, BlockID and type(fix, level or empty)
		SNA_fn_ulFWrite( &ucModule, sizeof(unsigned char), 1, _p_stFile );
		SNA_fn_ulFWrite( &ucBlock, sizeof(unsigned char), 1, _p_stFile );
		SNA_fn_ulFWrite( &_ucBlocType, sizeof(unsigned char), 1, _p_stFile );

		// Get current infos from MMG
		Mmg_fn_vGiveInformationBlock( ucModule, ucBlock, &stBloc );

		ulBeginBlock=(unsigned long)stBloc.p_cBeginBlock;
		ulEndBlock=(unsigned long)stBloc.p_cEndBlock;
		ulMaxMem=(unsigned long)stBloc.p_cMaxMem;

		SNA_fn_ulFWrite( &ulBeginBlock, 4, 1, _p_stFile );

		if( ulBeginBlock != (unsigned long)C_p_cBlockNotValidKey )
		{
			SNA_fn_ulFWrite( &ulEndBlock, 4, 1, _p_stFile );
			SNA_fn_ulFWrite( &(stBloc.p_cFirstFree), 4, 1, _p_stFile );
			SNA_fn_ulFWrite( &(stBloc.p_cMaxMem), 4, 1, _p_stFile );

			// Write block size
			ulBlockSize=0;
			SNA_fn_ulFWrite( &ulBlockSize, 4, 1, _p_stFile );
		}

		// Go to next element of the array
		_p_stArrayToSaveInfos++;
	}
}


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// Write the MMG header AND CONTENT of each block in the given array to file.
//
// Input:
//     _p_stArrayToSaveInfos : an array of block (ie: g_a_stModuleBlocToSaveInFix, etc)
//     _ulNbElem             : Number of elements in the array
//     _ucBlockType          : Type of block in the array (SNA_C_ucBlockOfFix, SNA_C_ucBlockOfLevel or SNA_C_ucEmptyBlock)
//     _p_stFile             : An opened file to write the infos.
void SNA_fn_vWriteMemoryBlocksOfArray(
    struct tdstModuleBloc_ *_p_stArrayToSaveInfos, unsigned long _ulNbElem,
    unsigned char _ucBlocType, struct SNA_tdstFile_ *_p_stFile )
{
	unsigned char ucModule, ucBlock;
	unsigned long ulBlockSize,ulBeginBlock,ulEndBlock,ulMaxMem;
	tdstBlockInfo stBloc;

	while( _ulNbElem-- )
	{
		ucModule = *(_p_stArrayToSaveInfos->p_ucModuleId);
		ucBlock = _p_stArrayToSaveInfos->ucBlockId;

		// Write block header :  ModuleID, BlockID and type(fix, level or empty)
		SNA_fn_ulFWrite( &ucModule, sizeof(unsigned char), 1, _p_stFile );
		SNA_fn_ulFWrite( &ucBlock, sizeof(unsigned char), 1, _p_stFile );
		SNA_fn_ulFWrite( &_ucBlocType, sizeof(unsigned char), 1, _p_stFile );


		Mmg_fn_vGiveInformationBlock( ucModule, ucBlock, &stBloc );

		ulBeginBlock=(unsigned long)stBloc.p_cBeginBlock;
		ulEndBlock=(unsigned long)stBloc.p_cEndBlock;
		ulMaxMem=(unsigned long)stBloc.p_cMaxMem;

		SNA_fn_ulFWrite( &ulBeginBlock, 4, 1, _p_stFile );

		if( ulBeginBlock != (unsigned long)C_p_cBlockNotValidKey )
		{
			SNA_fn_ulFWrite( &ulEndBlock, 4, 1, _p_stFile );
			SNA_fn_ulFWrite( &(stBloc.p_cFirstFree), 4, 1, _p_stFile );
			SNA_fn_ulFWrite( &(stBloc.p_cMaxMem), 4, 1, _p_stFile );

			// Write block size
			ulBlockSize=ulMaxMem-ulBeginBlock+1+8; // 8 : Remain place bloc info
			SNA_fn_ulFWrite( &ulBlockSize, 4, 1, _p_stFile );
			// Write the block
			SNA_fn_ulFWrite(stBloc.p_cBeginBlock,1,ulBlockSize,_p_stFile);
		}

		_p_stArrayToSaveInfos++;
	}
}

/*-----------------------------------------------------------------------------
 *  Description : Write all memory blocks to file
 *-----------------------------------------------------------------------------
 *  Input :         None
 *  Output :        None
 *-----------------------------------------------------------------------------
 *  Creation date : 03/09/97            Author : Michaël
 *-----------------------------------------------------------------------------
 *  Modification date : 29/09/97        Modification Author : CGHT
 *  Modifications :
 *---------------------------------------------------------------------------*/
void SNA_fn_vWriteAllMemoryBlocks(char *szFilename)
{
	struct SNA_tdstFile_ stSnaFile;
	DWORD dwBytesWritten;

	SNA_fn_vInitArrays();

#if defined(SNA_ENCRYPTION)
	SNA_fn_bFOpen(szFilename,SNA_C_ucWrite,SNA_C_ucUseEncryption,&stSnaFile);
#else
	SNA_fn_bFOpen(szFilename,SNA_C_ucWrite,0,&stSnaFile);
#endif

	// Write crypt key in file
	SNA_M_WRITEFILE(stSnaFile.hFile , &stSnaFile.ulCryptKey , sizeof(stSnaFile.ulCryptKey) , &dwBytesWritten, NULL);

	// First, write some infos on some block witch content will not be saved (for pointer destination detection only)
	SNA_fn_vWriteMemoryInfosOfArray(
	    g_a_stModuleBlocToSaveInfos, SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInfos),
	    SNA_C_ucEmptyBlock, &stSnaFile);

	// Then write the infos about the blocks that will be saved
	SNA_fn_vWriteMemoryInfosOfArray(
		  g_a_stModuleBlocToSaveInFix, SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInFix),
		  SNA_C_ucBlockOfFix, &stSnaFile );

	if( g_bSaveLoadFix )
	{
		// In the fix sna file, write the content of all fix memory blocks
		SNA_fn_vWriteMemoryBlocksOfArray(
		    g_a_stModuleBlocToSaveInFix, SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInFix),
		    SNA_C_ucBlockOfFix, &stSnaFile );
	}
	else
	{
		// In the level sna file, write the infos of the level mem blocks
		SNA_fn_vWriteMemoryInfosOfArray(
		    g_a_stModuleBlocToSaveInLevel, SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInLevel),
		    SNA_C_ucBlockOfLevel, &stSnaFile );
		// Then write the content of all level memory blocks
		SNA_fn_vWriteMemoryBlocksOfArray(
		    g_a_stModuleBlocToSaveInLevel, SNA_M_NbElementOfBlocArray(g_a_stModuleBlocToSaveInLevel),
		    SNA_C_ucBlockOfLevel, &stSnaFile );
	}

	SNA_fn_bFClose(&stSnaFile);
}

/*-----------------------------------------------------------------------------
 *  Description : Read all memory blocks from file
 *-----------------------------------------------------------------------------
 *  Input :         Name of the file
 *  Output :        None
 *-----------------------------------------------------------------------------
 *  Creation date : 03/09/97            Author : Michaël
 *-----------------------------------------------------------------------------
 *  Modification date :			        Modification Author :
 *  Modifications :
 *---------------------------------------------------------------------------*/
BOOL SNA_fn_bReadAllMemoryBlocks(char *szFilename)
{
	struct SNA_tdstFile_ stFile;
	ACP_tdxBool bOpenFileOK;
	unsigned char ucModule,ucBlock;
	unsigned long ulBlockSize,ulBeginBlock,ulEndBlock,ulFirstFree,ulMaxMem;
	unsigned long p_vPtr,*p_ulPtr;
	unsigned long i,j,k;
	unsigned char ucTargetModule, ucTargetBloc;
	tdstBlockInfo stBloc;
	unsigned long ulNbReloc = 0;
	DWORD dwBytesRead;
#ifndef RETAIL
	char szText[150];
	unsigned long ulNbPointerToUnknown = 0;
	unsigned long ulNbPointerToTmp = 0;
#endif

	// MR2711
	SNA_fn_vInitArrays();

#if defined(SNA_ENCRYPTION)
	ACP_M_OPENFILE(SNA_fn_bFOpen,bOpenFileOK,FALSE,szFilename,(szFilename,SNA_C_ucRead,SNA_C_ucUseEncryption,&stFile));
#else
	ACP_M_OPENFILE(SNA_fn_bFOpen,bOpenFileOK,FALSE,szFilename,(szFilename,SNA_C_ucRead,0,&stFile));
#endif

	if (! bOpenFileOK)
		return FALSE;

	stFile.bReadSuccess = SNA_M_READFILE(stFile.hFile , &stFile.ulCryptKey , sizeof(stFile.ulCryptKey) , &dwBytesRead , NULL);

#ifdef _DEBUG
	// Set relocation infos to invalid values.
	memset( g_a_bIsBlocRelocated, 55, C_ucNbOfMaxModule*10 );
#endif

	/// Load Reloc Table
	SNA_M_LoadUsedRelocationTable();

	// Take care of the progress bar
	VIG_fn_vAddToProgressBar(1);

	// Begin read from SNA file.
	while(stFile.bReadSuccess)
	{

		// Read Module ID and Block ID
		i=SNA_fn_ulFRead(&ucModule,1,1,&stFile);
		if(i==0)
		{
			break;
		}

		SNA_fn_ulFRead(&ucBlock,1,1,&stFile);

		// Read block location (fix or level)
		SNA_fn_ulFRead(&i,1,1,&stFile);

		// Get current info on this block
		Mmg_fn_vGiveInformationBlock(ucModule,ucBlock,&stBloc);

		g_a_bIsBlocRelocated[ucModule][ucBlock]=FALSE;

		// Read beginning of block
		SNA_fn_ulFRead(&ulBeginBlock,4,1,&stFile);

		// Test if block's emplacement has changed (=if it is relocated).
		if(stBloc.p_cBeginBlock!=(char *)ulBeginBlock)
		{

			// If so, compute the offset between the old block and the new one.
			g_a_bIsBlocRelocated[ucModule][ucBlock]=TRUE;
			g_a_ulOffset[ucModule][ucBlock]=(long)stBloc.p_cBeginBlock-(long)ulBeginBlock;

#ifndef RETAIL
			sprintf( szText, "Module %d Bloc %d is relocated from %lx to %lx\n", ucModule, ucBlock, ulBeginBlock, stBloc.p_cBeginBlock );
			OutputDebugString(szText);
#endif
		}
		else
		{
			// If it's not relocated, offset=0
			g_a_ulOffset[ucModule][ucBlock]=0;
		}

		// MR0310
		if ((char *)ulBeginBlock==C_p_cBlockNotValidKey)
			continue;

		SNA_fn_ulFRead(&ulEndBlock,4,1,&stFile);

		// If this block is to be loaded by sna, update some infos in MMG structures.
		// (FirstFree and MaxMem)
		SNA_fn_ulFRead(&ulFirstFree,4,1,&stFile);
		SNA_fn_ulFRead(&ulMaxMem,4,1,&stFile);

		SNA_fn_ulFRead(&ulBlockSize,4,1,&stFile);
		if( SNA_fn_bLoadThisModuleBloc(ucModule,ucBlock) && ulBlockSize>0 )
		{
			stBloc.p_cMaxMem=(char *)ulMaxMem+g_a_ulOffset[ucModule][ucBlock];
			if( ulFirstFree!= (unsigned long)C_p_cBlockNotValidKey )
				stBloc.p_cFirstFree=(char *)ulFirstFree+g_a_ulOffset[ucModule][ucBlock];

			// Read the block
			SNA_fn_ulFRead(stBloc.p_cBeginBlock,1,ulBlockSize,&stFile);

			// Save the updated memory block info for MMG.
			Mmg_fn_vSaveInformationBlock(ucModule,ucBlock,&stBloc);

			// Take care of the progress bar
			VIG_fn_vAddToProgressBar(1);

			// Now we are going to relocate each pointer in the current block.
			// Search reloc info for current block.
            for (k=0;k<SNA_g_PTCRelocationTable->ucBlocNumber;k++)
                if ((SNA_g_PTCRelocationTable->p_stBloc[k].ucModuleNumber==ucModule) &&
                    (SNA_g_PTCRelocationTable->p_stBloc[k].ucBlocNumber==ucBlock))
				{
					break;
				}

			// If there are reloc infos,
            if (k<SNA_g_PTCRelocationTable->ucBlocNumber)
			{
				// go throught the pointer table.
                for (j=0;j<SNA_g_PTCRelocationTable->p_stBloc[k].ulSize;j++)
				{
					// For each pointer to be relocated,
					// get his target module and block.
                    ucTargetModule=SNA_g_PTCRelocationTable->p_stBloc[k].p_stPtr[j].ucTargetModule;
                    ucTargetBloc=SNA_g_PTCRelocationTable->p_stBloc[k].p_stPtr[j].ucTargetBlock;

#ifdef _DEBUG
					// Make sure the source and dest block infos have been read.
					assert( g_a_bIsBlocRelocated[ucModule][ucBlock] != 55 );
					if( ucTargetModule < 0xf0 )
						assert( g_a_bIsBlocRelocated[ucTargetModule][ucTargetBloc] != 55 );
#endif

					p_vPtr=g_a_bIsBlocRelocated[ucModule][ucBlock]?
                    SNA_g_PTCRelocationTable->p_stBloc[k].p_stPtr[j].p_vPtr+g_a_ulOffset[ucModule][ucBlock]:
                    SNA_g_PTCRelocationTable->p_stBloc[k].p_stPtr[j].p_vPtr;
					p_ulPtr=(unsigned long *)p_vPtr;

					if (ucTargetModule == g_ucTMPModuleId)
					{
							// If pointer points to TMP, set it to NULL.
							(*p_ulPtr) = (unsigned long)NULL;       // GuS (28/09/98)
#ifndef RETAIL
							ulNbPointerToTmp ++;
#endif
					}
					else if (ucTargetModule==0xff)
					{
						// If that pointer point to an unknown place ... well... we let it do that !
#ifndef RETAIL
							ulNbPointerToUnknown ++;
#endif
					}
					else
					{
						(*p_ulPtr) += g_a_ulOffset[ucTargetModule][ucTargetBloc];
					}
				}

				// Take care of the progress bar
				VIG_fn_vAddToProgressBar(1);
			}
		}
		else if( ulBlockSize > 0 )
		{
			SNA_fn_bFseek( &stFile, ulBlockSize, SEEK_CUR );
		}

	}

    SNA_M_FreeRelocationTable();
	SNA_fn_bFClose(&stFile);

#ifndef RETAIL
	sprintf
		( szText,
			"Found %li pointers in %s that point to a TMP block.\n(Those pointers have been set to NULL)\n",
			ulNbPointerToTmp, szFilename );
	OutputDebugString( szText );
	sprintf
		( szText,
			"Found %li pointers in %s that point to an unknown place.\n(Those pointers have not been relocated)\n",
			ulNbPointerToUnknown, szFilename );
	OutputDebugString( szText );
#endif

	return TRUE;
}


#ifndef RETAIL
// For debug ONLY !

/*
	fn_vChangeWindowTitle("Checking MMG blocks...");
	//SNA_fn_vCheckAllMMGBlocks();
	fn_vChangeWindowTitle("Rayman II");

	static unsigned char ucDelay = 0;
	if( ucDelay++ >= 30 )
	{
		fn_vChangeWindowTitle("Checking MMG blocks...");
		//SNA_fn_vCheckAllMMGBlocks();
		fn_vChangeWindowTitle("Rayman II");
		ucDelay = 0;
	}


*/

// Function for checking integrity of a mem block.
// Block must be static and with free.
// This function can detect errors caused by memory overwrites.
void SNA_fn_vCheckMMGBlock( unsigned char _ucModule, unsigned char _ucBlock, unsigned char _ucLog )
{
	static unsigned long ulCallCount = 0;
	struct tdstBlockInfo_ stInfoMMG;
	long *p_lFree;
	long *p_lAlloc;
	long lSize, lNext;
	long lNbFree, lNbAlloc, lTotalFree, lTotalAlloc;
	char szFileName[255];
	FILE *p_xFile;

	lNbFree = lNbAlloc = lTotalFree = lTotalAlloc = 0;

	Mmg_fn_vGiveInformationBlock( _ucModule, _ucBlock, &stInfoMMG );

	// Can't check uninitialised block
	if( stInfoMMG.p_cBeginBlock == (char *)0xffffffff )
		return;

	// Can't check block without free
	if( stInfoMMG.p_cFirstFree == (char *)0xffffffff )
		return;

	p_lFree = (long *)stInfoMMG.p_cFirstFree;
	p_lAlloc = (long *)stInfoMMG.p_cBeginBlock;

	// FirstFree is out of block ?
	if( p_lFree )
		assert( (p_lFree >= (long *)(stInfoMMG.p_cBeginBlock)) && (p_lFree < (long *)(stInfoMMG.p_cEndBlock)) );

	// BeginBlock is greater than EndOfBlock ?
	assert( p_lAlloc < (long *)(stInfoMMG.p_cEndBlock) );

	while( p_lAlloc < (long *)(stInfoMMG.p_cEndBlock) )
	{
		lSize = *p_lAlloc << C_uwShiftAllocSize >> 2;

		// Size of allocated block is null or too big ?
		assert( lSize > 0 );

		// This is a free memory block.
		if( p_lAlloc == p_lFree )
		{
			lNext = *(p_lFree+1);
			// Size of free block is too big ?
			assert( (p_lFree+lSize >= (long *)(stInfoMMG.p_cBeginBlock)) && (p_lFree+lSize < (long *)(stInfoMMG.p_cEndBlock+4)) );
			if( lNext != 0 )
				// Next free is out of Block bounds ?
				assert( (lNext >= (long)(stInfoMMG.p_cBeginBlock)) && (lNext < (long)(stInfoMMG.p_cEndBlock)) );
			p_lFree = (long *)lNext;
			lNbFree++;
			lTotalFree += lSize;
		}
		else
		{
			// Allocated mem block goes beyong it's bounds ?
			if( p_lFree )
				assert( p_lAlloc+lSize <= p_lFree );
			// Allocated mem block size is too big ?
			assert( (p_lAlloc+lSize >= (long *)(stInfoMMG.p_cBeginBlock)) && (p_lAlloc+lSize < (long *)(stInfoMMG.p_cEndBlock+4)) );
			lNbAlloc++;
			lTotalAlloc += lSize;
		}

		p_lAlloc += lSize;
	}

	if( _ucLog )
	{
		sprintf( szFileName, "Block%02x%02x.log", _ucModule, _ucBlock );
		p_xFile = fopen( szFileName, "at" );
		if( p_xFile )
		{
			ulCallCount++;
			fprintf( p_xFile, "Block=#%02x%02x (Number=#%03d)\n", _ucModule, _ucBlock, ulCallCount );
			fprintf( p_xFile, "   Nb Alloc=%05d Total Allocated=%d\n", lNbAlloc, lTotalAlloc );
			fprintf( p_xFile, "   Nb Free=%05d  Total Free=%d\n", lNbFree, lTotalFree );
			fclose( p_xFile );
		}
	}

}


// Call CheckMMGBlock for each block.
void SNA_fn_vCheckAllMMGBlocks()
{
	unsigned char i, j;
	for( i=0; i<C_ucNbOfMaxModule; i++ )
		for( j=0; j<g_a_ucBlocksNbInModule[i]; j++ )
			SNA_fn_vCheckMMGBlock( i, j, 0 );
}


#endif // ! RETAIL

