Files
source/Standard Gaming Platform/MemMan.cpp
T

753 lines
19 KiB
C++

//**************************************************************************
//
// Filename : MemMan.cpp
//
// Purpose : function definitions for the memory manager
//
// Modification history :
//
// 11sep96:HJH - Creation
// 29may97:ARM - Fix & improve MemDebugCounter handling, logging of
// MemAlloc/MemFree, and reporting of any errors
//
//**************************************************************************
//**************************************************************************
//
// Includes
//
//**************************************************************************
//#ifdef JA2_PRECOMPILED_HEADERS
// #include "JA2 SGP ALL.H"
//#elif defined( WIZ8_PRECOMPILED_HEADERS )
// #include "WIZ8 SGP ALL.H"
//#else
#include "types.h"
#include <windows.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include "MemMan.h"
#include "Debug.h"
#include <stdio.h>
#ifdef _DEBUG
#include <crtdbg.h>
#endif
//#endif
#ifdef _DEBUG
//#define DEBUG_MEM_LEAKS // turns on tracking of every MemAlloc and MemFree!
#endif
//**************************************************************************
//
// Variables
//
//**************************************************************************
#ifdef JA2
#include "mousesystem.h"
#include "MessageBoxScreen.h"
STR16 gzJA2ScreenNames[] =
{
L"EDIT_SCREEN",
L"SAVING_SCREEN",
L"LOADING_SCREEN",
L"ERROR_SCREEN",
L"INIT_SCREEN",
L"GAME_SCREEN",
L"ANIEDIT_SCREEN",
L"PALEDIT_SCREEN",
L"DEBUG_SCREEN",
L"MAP_SCREEN",
L"LAPTOP_SCREEN",
L"LOADSAVE_SCREEN",
L"MAPUTILITY_SCREEN",
L"FADE_SCREEN",
L"MSG_BOX_SCREEN",
L"MAINMENU_SCREEN",
L"AUTORESOLVE_SCREEN",
L"SAVE_LOAD_SCREEN",
L"OPTIONS_SCREEN",
L"SHOPKEEPER_SCREEN",
L"SEX_SCREEN",
L"GAME_INIT_OPTIONS_SCREEN",
L"DEMO_EXIT_SCREEN",
L"INTRO_SCREEN",
L"CREDIT_SCREEN",
#ifdef JA2BETAVERSION
L"AIVIEWER_SCREEN",
L"QUEST_DEBUG_SCREEN",
#endif
};
#endif
#ifdef EXTREME_MEMORY_DEBUGGING
typedef struct MEMORY_NODE
{
PTR pBlock;
struct MEMORY_NODE *next, *prev;
UINT8 *pCode;
UINT32 uiSize;
}MEMORY_NODE;
MEMORY_NODE *gpMemoryHead = NULL;
MEMORY_NODE *gpMemoryTail = NULL;
UINT32 guiMemoryNodes = 0;
UINT32 guiTotalMemoryNodes = 0;
#endif
static BOOLEAN gfMemDebug = TRUE;
// debug variable for total memory currently allocated
UINT32 guiMemTotal = 0;
UINT32 guiMemAlloced = 0;
UINT32 guiMemFreed = 0;
UINT32 MemDebugCounter = 0;
BOOLEAN fMemManagerInit = FALSE;
//**************************************************************************
//
// Function Prototypes
//
//**************************************************************************
void DebugPrint( void );
//**************************************************************************
//
// Functions
//
//**************************************************************************
//**************************************************************************
//
// MemInit
//
//
//
// Parameter List :
// Return Value :
// Modification history :
//
// 12sep96:HJH -> modified for use by Wizardry
//
//**************************************************************************
BOOLEAN InitializeMemoryManager( void )
{
// Register the memory manager with the debugger
RegisterDebugTopic(TOPIC_MEMORY_MANAGER, "Memory Manager");
MemDebugCounter = 0;
guiMemTotal = 0;
guiMemAlloced = 0;
guiMemFreed = 0;
fMemManagerInit = TRUE;
#ifdef EXTREME_MEMORY_DEBUGGING
gpMemoryHead = NULL;
gpMemoryTail = NULL;
guiMemoryNodes = 0;
guiTotalMemoryNodes = 0;
#endif
return(TRUE);
}
//**************************************************************************
//
// MemDebug
//
// To set whether or not we should print debug info.
//
// Parameter List :
// Return Value :
// Modification history :
//
// 12sep96:HJH -> modified for use by Wizardry
//
//**************************************************************************
void MemDebug( BOOLEAN f )
{
gfMemDebug = f;
}
//**************************************************************************
//
// MemShutdown
//
// Shuts down the memory manager.
//
// Parameter List :
// Return Value :
// Modification history :
//
// 12sep96:HJH -> modified for use by Wizardry
//
//**************************************************************************
void ShutdownMemoryManager( void )
{
if ( MemDebugCounter != 0 )
{
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" "));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****"));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****"));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****"));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" "));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" >>>>> MEMORY LEAK DETECTED!!! <<<<< "));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("%d memory blocks still allocated", MemDebugCounter ));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("%d bytes memory total STILL allocated", guiMemTotal ));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("%d bytes memory total was allocated", guiMemAlloced));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("%d bytes memory total was freed", guiMemFreed));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" "));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****"));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****"));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("***** WARNING - WARNING - WARNING *****"));
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String(" "));
#ifndef EXTREME_MEMORY_DEBUGGING
#ifdef JA2BETAVERSION
{
FILE *fp;
fp = fopen( "MemLeakInfo.txt", "a" );
if( fp )
{
fprintf( fp, "\n\n" );
fprintf( fp, ">>>>> MEMORY LEAK DETECTED!!! <<<<<\n" );
fprintf( fp, " %d bytes memory total was allocated\n", guiMemAlloced );
fprintf( fp, "- %d bytes memory total was freed\n", guiMemFreed );
fprintf( fp, "_______________________________________________\n" );
fprintf( fp, "%d bytes memory total STILL allocated\n", guiMemTotal );
fprintf( fp, "%d memory blocks still allocated\n", MemDebugCounter );
fprintf( fp, "guiScreenExitedFrom = %S\n", gzJA2ScreenNames[ gMsgBox.uiExitScreen ] );
fprintf( fp, "\n\n" );
}
fclose( fp );
}
#endif
#endif
}
UnRegisterDebugTopic( TOPIC_MEMORY_MANAGER, "Memory Manager Un-initialized" );
fMemManagerInit = FALSE;
}
#ifdef _DEBUG
PTR MemAllocReal( UINT32 uiSize, const char *pcFile, INT32 iLine )
{
PTR ptr;
if( !uiSize )
{
return NULL;
}
if ( !fMemManagerInit )
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemAlloc: Warning -- Memory manager not initialized -- Line %d in %s", iLine, pcFile) );
ptr = _malloc_dbg( uiSize, _NORMAL_BLOCK, pcFile, iLine );
if (ptr != NULL)
{
guiMemTotal += uiSize;
guiMemAlloced += uiSize;
MemDebugCounter++;
}
else
{
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemAlloc failed: %d bytes (line %d file %s)", uiSize, iLine, pcFile) );
}
#ifdef DEBUG_MEM_LEAKS
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemAlloc %p: %d bytes (line %d file %s)", ptr, uiSize, iLine, pcFile) );
#endif
return( ptr );
}
void MemFreeReal( PTR ptr, const char *pcFile, INT32 iLine )
{
UINT32 uiSize;
if ( !fMemManagerInit )
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemFree: Warning -- Memory manager not initialized -- Line %d in %s", iLine, pcFile) );
if (ptr != NULL)
{
uiSize = _msize(ptr);
guiMemTotal -= uiSize;
guiMemFreed += uiSize;
_free_dbg( ptr, _NORMAL_BLOCK );
#ifdef DEBUG_MEM_LEAKS
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemFree %p: %d bytes (line %d file %s)", ptr, uiSize, iLine, pcFile) );
#endif
}
else
{
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemFree ERROR: NULL ptr received (line %d file %s)", iLine, pcFile) );
}
// count even a NULL ptr as a MemFree, not because it's really a memory leak, but because it is still an error of some
// sort (nobody should ever be freeing NULL pointers), and this will help in tracking it down if the above DbgMessage
// is not noticed.
MemDebugCounter--;
}
PTR MemReallocReal( PTR ptr, UINT32 uiSize, const char *pcFile, INT32 iLine )
{
PTR ptrNew;
UINT32 uiOldSize;
if ( !fMemManagerInit )
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemRealloc: Warning -- Memory manager not initialized -- Line %d in %s", iLine, pcFile) );
if(ptr != NULL)
{
uiOldSize = _msize(ptr);
guiMemTotal -= uiOldSize;
guiMemFreed += uiOldSize;
MemDebugCounter--;
}
// Note that the ptr changes to ptrNew...
ptrNew = _realloc_dbg( ptr, uiSize, _NORMAL_BLOCK, pcFile, iLine );
if (ptrNew == NULL)
{
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemReAlloc failed: ptr %d, %d -> %d bytes (line %d file %s)", ptr, uiOldSize, uiSize, iLine, pcFile) );
if ( uiSize != 0 )
{
// ptr is left untouched, so undo the math above
guiMemTotal += uiOldSize;
guiMemFreed -= uiOldSize;
MemDebugCounter++;
}
}
else
{
#ifdef DEBUG_MEM_LEAKS
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemRealloc %p: Resizing %d bytes to %d bytes (line %d file %s) - New ptr %p", ptr, uiOldSize, uiSize, iLine, pcFile, ptrNew ) );
#endif
guiMemTotal += uiSize;
guiMemAlloced += uiSize;
MemDebugCounter++;
}
return( ptrNew );
}
#endif
PTR MemAllocLocked( UINT32 uiSize )
{
PTR ptr;
if ( !fMemManagerInit )
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemAllocLocked: Warning -- Memory manager not initialized!!! ") );
ptr = VirtualAlloc( NULL, uiSize, MEM_COMMIT, PAGE_READWRITE );
if ( ptr )
{
VirtualLock( ptr, uiSize );
guiMemTotal += uiSize;
guiMemAlloced += uiSize;
MemDebugCounter++;
}
else
{
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemAllocLocked failed: %d bytes", uiSize) );
}
#ifdef DEBUG_MEM_LEAKS
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemAllocLocked %p: %d bytes", ptr, uiSize) );
#endif
return( ptr );
}
void MemFreeLocked( PTR ptr, UINT32 uiSize )
{
if ( !fMemManagerInit )
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemFreeLocked: Warning -- Memory manager not initialized!!! ") );
if (ptr != NULL)
{
VirtualUnlock( ptr, uiSize );
VirtualFree( ptr, uiSize, MEM_RELEASE );
guiMemTotal -= uiSize;
guiMemFreed += uiSize;
}
else
{
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_0, String("MemFreeLocked ERROR: NULL ptr received, size %d", uiSize) );
}
// count even a NULL ptr as a MemFree, not because it's really a memory leak, but because it is still an error of some
// sort (nobody should ever be freeing NULL pointers), and this will help in tracking it down if the above DbgMessage
// is not noticed.
MemDebugCounter--;
#ifdef DEBUG_MEM_LEAKS
DbgMessage( TOPIC_MEMORY_MANAGER, DBG_LEVEL_1, String("MemFreeLocked %p", ptr) );
#endif
}
//**************************************************************************
//
// MemGetFree
//
//
//
// Parameter List :
// Return Value :
// Modification history :
//
// ??sep96:HJH -> modified for use by Wizardry
//
//**************************************************************************
UINT32 MemGetFree( void )
{
MEMORYSTATUS ms;
ms.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus( &ms );
return( ms.dwAvailPhys );
}
//**************************************************************************
//
// MemGetTotalSystem
//
//
//
// Parameter List :
// Return Value :
// Modification history :
//
// May98:HJH -> Carter
//
//**************************************************************************
UINT32 MemGetTotalSystem( void )
{
MEMORYSTATUS ms;
ms.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus( &ms );
return( ms.dwTotalPhys );
}
//**************************************************************************
//
// MemCheckPool
//
//
//
// Parameter List :
// Return Value :
// Modification history :
//
// 23sep96:HJH -> modified for use by Wizardry
//
//**************************************************************************
BOOLEAN MemCheckPool( void )
{
BOOLEAN fRet = TRUE;
#ifdef _DEBUG
fRet = _CrtCheckMemory();
Assert( fRet );
#endif
return(fRet);
}
#ifdef EXTREME_MEMORY_DEBUGGING
PTR MemAllocXDebug( UINT32 size, const char *szCodeString, INT32 iLineNum, void *pSpecial )
{
PTR ptr;
UINT16 usLength;
UINT8 str[70];
UINT8 *pStr;
if( !size )
{
return NULL;
}
if( !pSpecial )
{
ptr = malloc( size );
}
else
{
ptr = pSpecial;
}
if( ptr )
{
// Set into video object list
if( gpMemoryHead )
{ //Add node after tail
gpMemoryTail->next = (MEMORY_NODE*)malloc( sizeof( MEMORY_NODE ) );
Assert( gpMemoryTail->next ); //out of memory?
gpMemoryTail->next->prev = gpMemoryTail;
gpMemoryTail->next->next = NULL;
gpMemoryTail = gpMemoryTail->next;
}
else
{ //new list
gpMemoryHead = (MEMORY_NODE*)malloc( sizeof( MEMORY_NODE ) );
Assert( gpMemoryHead ); //out of memory?
gpMemoryHead->prev = gpMemoryHead->next = NULL;
gpMemoryTail = gpMemoryHead;
}
//record the code location of the calling creating function.
pStr = strrchr( szCodeString, '\\' );
pStr++;
sprintf( str, "%s -- line(%d)", pStr, iLineNum );
usLength = strlen( str ) + 1;
gpMemoryTail->pCode = (UINT8*)malloc( usLength );
memset( gpMemoryTail->pCode, 0, usLength );
strcpy( gpMemoryTail->pCode, str );
//record the size
gpMemoryTail->uiSize = size;
//Set the hVObject into the node.
gpMemoryTail->pBlock = ptr;
guiMemoryNodes++;
guiTotalMemoryNodes++;
}
return( ptr );
}
void MemFreeXDebug( PTR ptr, const char *szCodeString, INT32 iLineNum, void *pSpecial )
{
MEMORY_NODE *curr;
if( ptr )
{
curr = gpMemoryHead;
while( curr )
{
if( curr->pBlock == ptr )
{ //Found the node, so detach it and delete it.
if( !pSpecial )
{
free( ptr );
}
if( curr == gpMemoryHead )
{ //Advance the head, because we are going to remove the head node.
gpMemoryHead = gpMemoryHead->next;
}
if( curr == gpMemoryTail )
{ //Back up the tail, because we are going to remove the tail node.
gpMemoryTail = gpMemoryTail->prev;
}
//Detach the node from the vobject list
if( curr->next )
{ //Make the prev node point to the next
curr->next->prev = curr->prev;
}
if( curr->prev )
{ //Make the next node point to the prev
curr->prev->next = curr->next;
}
//The node is now detached. Now deallocate it.
free( curr );
curr = NULL;
guiMemoryNodes--;
return;
}
curr = curr->next;
}
}
}
PTR MemReallocXDebug( PTR ptr, UINT32 size, const char *szCodeString, INT32 iLineNum, void *pSpecial )
{
MEMORY_NODE *curr;
PTR ptrNew;
UINT16 usLength;
UINT8 str[70];
UINT8 *pStr;
if( !ptr && size )
{
return MemAllocXDebug( size, szCodeString, iLineNum, pSpecial );
}
curr = gpMemoryHead;
while( curr )
{
if( curr->pBlock == ptr )
{
// Note that the ptr changes to ptrNew...
if( !pSpecial )
{
ptrNew = realloc( ptr, size );
}
else
{
ptrNew = pSpecial;
}
if( ptrNew )
{
curr->pBlock = ptrNew;
curr->uiSize = size;
free( curr->pCode );
//record the code location of the calling creating function.
pStr = strrchr( szCodeString, '\\' );
pStr++;
sprintf( str, "%s -- line(%d)", pStr, iLineNum );
usLength = strlen( str ) + 1;
curr->pCode = (UINT8*)malloc( usLength );
memset( curr->pCode, 0, usLength );
strcpy( curr->pCode, str );
}
else
{
ptr = ptr;
}
return ptrNew;
}
curr = curr->next;
}
return 0;
}
typedef struct DUMPFILENAME
{
UINT8 str[70];
}DUMPFILENAME;
void DumpMemoryInfoIntoFile( UINT8 *filename, BOOLEAN fAppend )
{
MEMORY_NODE *curr;
FILE *fp;
DUMPFILENAME *pCode;
UINT32 *puiCounter, *puiSize;
UINT8 tempCode[ 70 ];
UINT32 i, uiUniqueID, uiTotalKbWasted = 0, uiBytesRemainder = 0;
BOOLEAN fFound;
if( fAppend )
{
fp = fopen( filename, "a" );
}
else
{
fp = fopen( filename, "w" );
}
Assert( fp );
if( !guiMemoryNodes )
{
fprintf( fp, "NO MEMORY LEAKS DETECTED! CONGRATULATIONS!\n" );
fclose( fp );
return;
}
//Allocate enough strings and counters for each node.
pCode = (DUMPFILENAME*)malloc( sizeof( DUMPFILENAME ) * guiMemoryNodes );
memset( pCode, 0, sizeof( DUMPFILENAME ) * guiMemoryNodes );
puiSize = (UINT32*)malloc( 4 * guiMemoryNodes );
memset( puiSize, 0, 4 * guiMemoryNodes );
puiCounter = (UINT32*)malloc( 4 * guiMemoryNodes );
memset( puiCounter, 0, 4 * guiMemoryNodes );
//Loop through the list and record every unique filename and count them
uiUniqueID = 0;
curr = gpMemoryHead;
while( curr )
{
strcpy( tempCode, curr->pCode );
fFound = FALSE;
for( i = 0; i < uiUniqueID; i++ )
{
if( !_stricmp( tempCode, pCode[i].str ) )
{ //same string
fFound = TRUE;
(puiCounter[ i ])++;
(puiSize[ i ]) += curr->uiSize;
break;
}
}
if( !fFound )
{
strcpy( pCode[i].str, tempCode );
(puiSize[ i ]) += curr->uiSize;
(puiCounter[ i ])++;
uiUniqueID++;
}
curr = curr->next;
}
//Now dump the info.
fprintf( fp, "--------------------------------------------------------------------------------\n" );
fprintf( fp, "%d unique memory allocation locations exist in %d memory nodes\n", uiUniqueID, guiMemoryNodes );
fprintf( fp, "--------------------------------------------------------------------------------\n" );
for( i = 0; i < uiUniqueID; i++ )
{
fprintf( fp, "%d occurrences of %s (total size %d bytes)\n", puiCounter[i], pCode[i].str, puiSize[i] );
uiBytesRemainder += puiSize[i];
if( uiBytesRemainder >= 1024 )
{
uiTotalKbWasted += uiBytesRemainder/1024;
uiBytesRemainder %= 1024;
}
}
fprintf( fp, "--------------------------------------------------------------------------------\n" );
fprintf( fp, "%dKB of memory total wasn't cleaned up!\n", uiTotalKbWasted );
fprintf( fp, "--------------------------------------------------------------------------------\n" );
fclose( fp );
//Free all memory associated with this operation.
free( pCode );
free( puiCounter );
free( puiSize );
}
BOOLEAN _AddAndRecordMemAlloc( UINT32 size, UINT32 uiLineNum, UINT8 *pSourceFile )
{
return 0;
}
#endif