/*========================================================================= * 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_stArrayp_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;kucBlocNumber;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 (kucBlocNumber) { // go throught the pointer table. for (j=0;jp_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