DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

win_shared.cpp (18115B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../../idlib/precompiled.h"
     31 
     32 #include "win_local.h"
     33 #include <lmerr.h>
     34 #include <lmcons.h>
     35 #include <lmwksta.h>
     36 #include <errno.h>
     37 #include <fcntl.h>
     38 #include <direct.h>
     39 #include <io.h>
     40 #include <conio.h>
     41 #undef StrCmpN
     42 #undef StrCmpNI
     43 #undef StrCmpI
     44 #include <atlbase.h>
     45 
     46 #include <comdef.h>
     47 #include <comutil.h>
     48 #include <Wbemidl.h>
     49 
     50 #pragma comment (lib, "wbemuuid.lib")
     51 
     52 #pragma warning(disable:4740)	// warning C4740: flow in or out of inline asm code suppresses global optimization
     53 
     54 /*
     55 ================
     56 Sys_Milliseconds
     57 ================
     58 */
     59 int Sys_Milliseconds() {
     60 	static DWORD sys_timeBase = timeGetTime();
     61 	return timeGetTime() - sys_timeBase;
     62 }
     63 
     64 /*
     65 ========================
     66 Sys_Microseconds
     67 ========================
     68 */
     69 uint64 Sys_Microseconds() {
     70 	static uint64 ticksPerMicrosecondTimes1024 = 0;
     71 
     72 	if ( ticksPerMicrosecondTimes1024 == 0 ) {
     73 		ticksPerMicrosecondTimes1024 = ( (uint64)Sys_ClockTicksPerSecond() << 10 ) / 1000000;
     74 		assert( ticksPerMicrosecondTimes1024 > 0 );
     75 	}
     76 
     77 	return ((uint64)( (int64)Sys_GetClockTicks() << 10 )) / ticksPerMicrosecondTimes1024;
     78 }
     79 
     80 /*
     81 ================
     82 Sys_GetSystemRam
     83 
     84 	returns amount of physical memory in MB
     85 ================
     86 */
     87 int Sys_GetSystemRam() {
     88 	MEMORYSTATUSEX statex;
     89 	statex.dwLength = sizeof ( statex );
     90 	GlobalMemoryStatusEx (&statex);
     91 	int physRam = statex.ullTotalPhys / ( 1024 * 1024 );
     92 	// HACK: For some reason, ullTotalPhys is sometimes off by a meg or two, so we round up to the nearest 16 megs
     93 	physRam = ( physRam + 8 ) & ~15;
     94 	return physRam;
     95 }
     96 
     97 
     98 /*
     99 ================
    100 Sys_GetDriveFreeSpace
    101 returns in megabytes
    102 ================
    103 */
    104 int Sys_GetDriveFreeSpace( const char *path ) {
    105 	DWORDLONG lpFreeBytesAvailable;
    106 	DWORDLONG lpTotalNumberOfBytes;
    107 	DWORDLONG lpTotalNumberOfFreeBytes;
    108 	int ret = 26;
    109 	//FIXME: see why this is failing on some machines
    110 	if ( ::GetDiskFreeSpaceEx( path, (PULARGE_INTEGER)&lpFreeBytesAvailable, (PULARGE_INTEGER)&lpTotalNumberOfBytes, (PULARGE_INTEGER)&lpTotalNumberOfFreeBytes ) ) {
    111 		ret = ( double )( lpFreeBytesAvailable ) / ( 1024.0 * 1024.0 );
    112 	}
    113 	return ret;
    114 }
    115 
    116 /*
    117 ========================
    118 Sys_GetDriveFreeSpaceInBytes
    119 ========================
    120 */
    121 int64 Sys_GetDriveFreeSpaceInBytes( const char * path ) {
    122 	DWORDLONG lpFreeBytesAvailable;
    123 	DWORDLONG lpTotalNumberOfBytes;
    124 	DWORDLONG lpTotalNumberOfFreeBytes;
    125 	int64 ret = 1;
    126 	//FIXME: see why this is failing on some machines
    127 	if ( ::GetDiskFreeSpaceEx( path, (PULARGE_INTEGER)&lpFreeBytesAvailable, (PULARGE_INTEGER)&lpTotalNumberOfBytes, (PULARGE_INTEGER)&lpTotalNumberOfFreeBytes ) ) {
    128 		ret = lpFreeBytesAvailable;
    129 	}
    130 	return ret;
    131 }
    132 
    133 /*
    134 ================
    135 Sys_GetVideoRam
    136 returns in megabytes
    137 ================
    138 */
    139 int Sys_GetVideoRam() {
    140 	unsigned int retSize = 64;
    141 
    142 	CComPtr<IWbemLocator> spLoc = NULL;
    143 	HRESULT hr = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_SERVER, IID_IWbemLocator, ( LPVOID * ) &spLoc );
    144 	if ( hr != S_OK || spLoc == NULL ) {
    145 		return retSize;
    146 	}
    147 
    148 	CComBSTR bstrNamespace( _T( "\\\\.\\root\\CIMV2" ) );
    149 	CComPtr<IWbemServices> spServices;
    150 
    151 	// Connect to CIM
    152 	hr = spLoc->ConnectServer( bstrNamespace, NULL, NULL, 0, NULL, 0, 0, &spServices );
    153 	if ( hr != WBEM_S_NO_ERROR ) {
    154 		return retSize;
    155 	}
    156 
    157 	// Switch the security level to IMPERSONATE so that provider will grant access to system-level objects.  
    158 	hr = CoSetProxyBlanket( spServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );
    159 	if ( hr != S_OK ) {
    160 		return retSize;
    161 	}
    162 
    163 	// Get the vid controller
    164 	CComPtr<IEnumWbemClassObject> spEnumInst = NULL;
    165 	hr = spServices->CreateInstanceEnum( CComBSTR( "Win32_VideoController" ), WBEM_FLAG_SHALLOW, NULL, &spEnumInst ); 
    166 	if ( hr != WBEM_S_NO_ERROR || spEnumInst == NULL ) {
    167 		return retSize;
    168 	}
    169 
    170 	ULONG uNumOfInstances = 0;
    171 	CComPtr<IWbemClassObject> spInstance = NULL;
    172 	hr = spEnumInst->Next( 10000, 1, &spInstance, &uNumOfInstances );
    173 
    174 	if ( hr == S_OK && spInstance ) {
    175 		// Get properties from the object
    176 		CComVariant varSize;
    177 		hr = spInstance->Get( CComBSTR( _T( "AdapterRAM" ) ), 0, &varSize, 0, 0 );
    178 		if ( hr == S_OK ) {
    179 			retSize = varSize.intVal / ( 1024 * 1024 );
    180 			if ( retSize == 0 ) {
    181 				retSize = 64;
    182 			}
    183 		}
    184 	}
    185 	return retSize;
    186 }
    187 
    188 /*
    189 ================
    190 Sys_GetCurrentMemoryStatus
    191 
    192 	returns OS mem info
    193 	all values are in kB except the memoryload
    194 ================
    195 */
    196 void Sys_GetCurrentMemoryStatus( sysMemoryStats_t &stats ) {
    197 	MEMORYSTATUSEX statex = {};
    198 	unsigned __int64 work;
    199 
    200 	statex.dwLength = sizeof( statex );
    201 	GlobalMemoryStatusEx( &statex );
    202 
    203 	memset( &stats, 0, sizeof( stats ) );
    204 
    205 	stats.memoryLoad = statex.dwMemoryLoad;
    206 
    207 	work = statex.ullTotalPhys >> 20;
    208 	stats.totalPhysical = *(int*)&work;
    209 
    210 	work = statex.ullAvailPhys >> 20;
    211 	stats.availPhysical = *(int*)&work;
    212 
    213 	work = statex.ullAvailPageFile >> 20;
    214 	stats.availPageFile = *(int*)&work;
    215 
    216 	work = statex.ullTotalPageFile >> 20;
    217 	stats.totalPageFile = *(int*)&work;
    218 
    219 	work = statex.ullTotalVirtual >> 20;
    220 	stats.totalVirtual = *(int*)&work;
    221 
    222 	work = statex.ullAvailVirtual >> 20;
    223 	stats.availVirtual = *(int*)&work;
    224 
    225 	work = statex.ullAvailExtendedVirtual >> 20;
    226 	stats.availExtendedVirtual = *(int*)&work;
    227 }
    228 
    229 /*
    230 ================
    231 Sys_LockMemory
    232 ================
    233 */
    234 bool Sys_LockMemory( void *ptr, int bytes ) {
    235 	return ( VirtualLock( ptr, (SIZE_T)bytes ) != FALSE );
    236 }
    237 
    238 /*
    239 ================
    240 Sys_UnlockMemory
    241 ================
    242 */
    243 bool Sys_UnlockMemory( void *ptr, int bytes ) {
    244 	return ( VirtualUnlock( ptr, (SIZE_T)bytes ) != FALSE );
    245 }
    246 
    247 /*
    248 ================
    249 Sys_SetPhysicalWorkMemory
    250 ================
    251 */
    252 void Sys_SetPhysicalWorkMemory( int minBytes, int maxBytes ) {
    253 	::SetProcessWorkingSetSize( GetCurrentProcess(), minBytes, maxBytes );
    254 }
    255 
    256 /*
    257 ================
    258 Sys_GetCurrentUser
    259 ================
    260 */
    261 char *Sys_GetCurrentUser() {
    262 	static char s_userName[1024];
    263 	unsigned long size = sizeof( s_userName );
    264 
    265 
    266 	if ( !GetUserName( s_userName, &size ) ) {
    267 		strcpy( s_userName, "player" );
    268 	}
    269 
    270 	if ( !s_userName[0] ) {
    271 		strcpy( s_userName, "player" );
    272 	}
    273 
    274 	return s_userName;
    275 }	
    276 
    277 
    278 /*
    279 ===============================================================================
    280 
    281 	Call stack
    282 
    283 ===============================================================================
    284 */
    285 
    286 
    287 #define PROLOGUE_SIGNATURE 0x00EC8B55
    288 
    289 #include <dbghelp.h>
    290 
    291 const int UNDECORATE_FLAGS =	UNDNAME_NO_MS_KEYWORDS |
    292 								UNDNAME_NO_ACCESS_SPECIFIERS |
    293 								UNDNAME_NO_FUNCTION_RETURNS |
    294 								UNDNAME_NO_ALLOCATION_MODEL |
    295 								UNDNAME_NO_ALLOCATION_LANGUAGE |
    296 								UNDNAME_NO_MEMBER_TYPE;
    297 
    298 #if defined(_DEBUG) && 1
    299 
    300 typedef struct symbol_s {
    301 	int					address;
    302 	char *				name;
    303 	struct symbol_s *	next;
    304 } symbol_t;
    305 
    306 typedef struct module_s {
    307 	int					address;
    308 	char *				name;
    309 	symbol_t *			symbols;
    310 	struct module_s *	next;
    311 } module_t;
    312 
    313 module_t *modules;
    314 
    315 /*
    316 ==================
    317 SkipRestOfLine
    318 ==================
    319 */
    320 void SkipRestOfLine( const char **ptr ) {
    321 	while( (**ptr) != '\0' && (**ptr) != '\n' && (**ptr) != '\r' ) {
    322 		(*ptr)++;
    323 	}
    324 	while( (**ptr) == '\n' || (**ptr) == '\r' ) {
    325 		(*ptr)++;
    326 	}
    327 }
    328 
    329 /*
    330 ==================
    331 SkipWhiteSpace
    332 ==================
    333 */
    334 void SkipWhiteSpace( const char **ptr ) {
    335 	while( (**ptr) == ' ' ) {
    336 		(*ptr)++;
    337 	}
    338 }
    339 
    340 /*
    341 ==================
    342 ParseHexNumber
    343 ==================
    344 */
    345 int ParseHexNumber( const char **ptr ) {
    346 	int n = 0;
    347 	while( (**ptr) >= '0' && (**ptr) <= '9' || (**ptr) >= 'a' && (**ptr) <= 'f' ) {
    348 		n <<= 4;
    349 		if ( **ptr >= '0' && **ptr <= '9' ) {
    350 			n |= ( (**ptr) - '0' );
    351 		} else {
    352 			n |= 10 + ( (**ptr) - 'a' );
    353 		}
    354 		(*ptr)++;
    355 	}
    356 	return n;
    357 }
    358 
    359 /*
    360 ==================
    361 Sym_Init
    362 ==================
    363 */
    364 void Sym_Init( long addr ) {
    365 	TCHAR moduleName[MAX_STRING_CHARS];
    366 	MEMORY_BASIC_INFORMATION mbi;
    367 
    368 	VirtualQuery( (void*)addr, &mbi, sizeof(mbi) );
    369 
    370 	GetModuleFileName( (HMODULE)mbi.AllocationBase, moduleName, sizeof( moduleName ) );
    371 
    372 	char *ext = moduleName + strlen( moduleName );
    373 	while( ext > moduleName && *ext != '.' ) {
    374 		ext--;
    375 	}
    376 	if ( ext == moduleName ) {
    377 		strcat( moduleName, ".map" );
    378 	} else {
    379 		strcpy( ext, ".map" );
    380 	}
    381 
    382 	module_t *module = (module_t *) malloc( sizeof( module_t ) );
    383 	module->name = (char *) malloc( strlen( moduleName ) + 1 );
    384 	strcpy( module->name, moduleName );
    385 	module->address = (int)mbi.AllocationBase;
    386 	module->symbols = NULL;
    387 	module->next = modules;
    388 	modules = module;
    389 
    390 	FILE * fp = fopen( moduleName, "rb" );
    391 	if ( fp == NULL ) {
    392 		return;
    393 	}
    394 
    395 	int pos = ftell( fp );
    396 	fseek( fp, 0, SEEK_END );
    397 	int length = ftell( fp );
    398 	fseek( fp, pos, SEEK_SET );
    399 
    400 	char *text = (char *) malloc( length+1 );
    401 	fread( text, 1, length, fp );
    402 	text[length] = '\0';
    403 	fclose( fp );
    404 
    405 	const char *ptr = text;
    406 
    407 	// skip up to " Address" on a new line
    408 	while( *ptr != '\0' ) {
    409 		SkipWhiteSpace( &ptr );
    410 		if ( idStr::Cmpn( ptr, "Address", 7 ) == 0 ) {
    411 			SkipRestOfLine( &ptr );
    412 			break;
    413 		}
    414 		SkipRestOfLine( &ptr );
    415 	}
    416 
    417 	int symbolAddress;
    418 	int symbolLength;
    419 	char symbolName[MAX_STRING_CHARS];
    420 	symbol_t *symbol;
    421 
    422 	// parse symbols
    423 	while( *ptr != '\0' ) {
    424 
    425 		SkipWhiteSpace( &ptr );
    426 
    427 		ParseHexNumber( &ptr );
    428 		if ( *ptr == ':' ) {
    429 			ptr++;
    430 		} else {
    431 			break;
    432 		}
    433 		ParseHexNumber( &ptr );
    434 
    435 		SkipWhiteSpace( &ptr );
    436 
    437 		// parse symbol name
    438 		symbolLength = 0;
    439 		while( *ptr != '\0' && *ptr != ' ' ) {
    440 			symbolName[symbolLength++] = *ptr++;
    441 			if ( symbolLength >= sizeof( symbolName ) - 1 ) {
    442 				break;
    443 			}
    444 		}
    445 		symbolName[symbolLength++] = '\0';
    446 
    447 		SkipWhiteSpace( &ptr );
    448 
    449 		// parse symbol address
    450 		symbolAddress = ParseHexNumber( &ptr );
    451 
    452 		SkipRestOfLine( &ptr );
    453 
    454 		symbol = (symbol_t *) malloc( sizeof( symbol_t ) );
    455 		symbol->name = (char *) malloc( symbolLength );
    456 		strcpy( symbol->name, symbolName );
    457 		symbol->address = symbolAddress;
    458 		symbol->next = module->symbols;
    459 		module->symbols = symbol;
    460 	}
    461 
    462 	free( text );
    463 }
    464 
    465 /*
    466 ==================
    467 Sym_Shutdown
    468 ==================
    469 */
    470 void Sym_Shutdown() {
    471 	module_t *m;
    472 	symbol_t *s;
    473 
    474 	for ( m = modules; m != NULL; m = modules ) {
    475 		modules = m->next;
    476 		for ( s = m->symbols; s != NULL; s = m->symbols ) {
    477 			m->symbols = s->next;
    478 			free( s->name );
    479 			free( s );
    480 		}
    481 		free( m->name );
    482 		free( m );
    483 	}
    484 	modules = NULL;
    485 }
    486 
    487 /*
    488 ==================
    489 Sym_GetFuncInfo
    490 ==================
    491 */
    492 void Sym_GetFuncInfo( long addr, idStr &module, idStr &funcName ) {
    493 	MEMORY_BASIC_INFORMATION mbi;
    494 	module_t *m;
    495 	symbol_t *s;
    496 
    497 	VirtualQuery( (void*)addr, &mbi, sizeof(mbi) );
    498 
    499 	for ( m = modules; m != NULL; m = m->next ) {
    500 		if ( m->address == (int) mbi.AllocationBase ) {
    501 			break;
    502 		}
    503 	}
    504 	if ( !m ) {
    505 		Sym_Init( addr );
    506 		m = modules;
    507 	}
    508 
    509 	for ( s = m->symbols; s != NULL; s = s->next ) {
    510 		if ( s->address == addr ) {
    511 
    512 			char undName[MAX_STRING_CHARS];
    513 			if ( UnDecorateSymbolName( s->name, undName, sizeof(undName), UNDECORATE_FLAGS ) ) {
    514 				funcName = undName;
    515 			} else {
    516 				funcName = s->name;
    517 			}
    518 			for ( int i = 0; i < funcName.Length(); i++ ) {
    519 				if ( funcName[i] == '(' ) {
    520 					funcName.CapLength( i );
    521 					break;
    522 				}
    523 			}
    524 			module = m->name;
    525 			return;
    526 		}
    527 	}
    528 
    529 	sprintf( funcName, "0x%08x", addr );
    530 	module = "";
    531 }
    532 
    533 #elif defined(_DEBUG)
    534 
    535 DWORD lastAllocationBase = -1;
    536 HANDLE processHandle;
    537 idStr lastModule;
    538 
    539 /*
    540 ==================
    541 Sym_Init
    542 ==================
    543 */
    544 void Sym_Init( long addr ) {
    545 	TCHAR moduleName[MAX_STRING_CHARS];
    546 	TCHAR modShortNameBuf[MAX_STRING_CHARS];
    547 	MEMORY_BASIC_INFORMATION mbi;
    548 
    549 	if ( lastAllocationBase != -1 ) {
    550 		Sym_Shutdown();
    551 	}
    552 
    553 	VirtualQuery( (void*)addr, &mbi, sizeof(mbi) );
    554 
    555 	GetModuleFileName( (HMODULE)mbi.AllocationBase, moduleName, sizeof( moduleName ) );
    556 	_splitpath( moduleName, NULL, NULL, modShortNameBuf, NULL );
    557 	lastModule = modShortNameBuf;
    558 
    559 	processHandle = GetCurrentProcess();
    560 	if ( !SymInitialize( processHandle, NULL, FALSE ) ) {
    561 		return;
    562 	}
    563 	if ( !SymLoadModule( processHandle, NULL, moduleName, NULL, (DWORD)mbi.AllocationBase, 0 ) ) {
    564 		SymCleanup( processHandle );
    565 		return;
    566 	}
    567 
    568 	SymSetOptions( SymGetOptions() & ~SYMOPT_UNDNAME );
    569 
    570 	lastAllocationBase = (DWORD) mbi.AllocationBase;
    571 }
    572 
    573 /*
    574 ==================
    575 Sym_Shutdown
    576 ==================
    577 */
    578 void Sym_Shutdown() {
    579 	SymUnloadModule( GetCurrentProcess(), lastAllocationBase );
    580 	SymCleanup( GetCurrentProcess() );
    581 	lastAllocationBase = -1;
    582 }
    583 
    584 /*
    585 ==================
    586 Sym_GetFuncInfo
    587 ==================
    588 */
    589 void Sym_GetFuncInfo( long addr, idStr &module, idStr &funcName ) {
    590 	MEMORY_BASIC_INFORMATION mbi;
    591 
    592 	VirtualQuery( (void*)addr, &mbi, sizeof(mbi) );
    593 
    594 	if ( (DWORD) mbi.AllocationBase != lastAllocationBase ) {
    595 		Sym_Init( addr );
    596 	}
    597 
    598 	BYTE symbolBuffer[ sizeof(IMAGEHLP_SYMBOL) + MAX_STRING_CHARS ];
    599 	PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)&symbolBuffer[0];
    600 	pSymbol->SizeOfStruct = sizeof(symbolBuffer);
    601 	pSymbol->MaxNameLength = 1023;
    602 	pSymbol->Address = 0;
    603 	pSymbol->Flags = 0;
    604 	pSymbol->Size =0;
    605 
    606 	DWORD symDisplacement = 0;
    607 	if ( SymGetSymFromAddr( processHandle, addr, &symDisplacement, pSymbol ) ) {
    608 		// clean up name, throwing away decorations that don't affect uniqueness
    609 	    char undName[MAX_STRING_CHARS];
    610 		if ( UnDecorateSymbolName( pSymbol->Name, undName, sizeof(undName), UNDECORATE_FLAGS ) ) {
    611 			funcName = undName;
    612 		} else {
    613 			funcName = pSymbol->Name;
    614 		}
    615 		module = lastModule;
    616 	}
    617 	else {
    618 		LPVOID lpMsgBuf;
    619 		FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    620 						NULL,
    621 						GetLastError(),
    622 						MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    623 						(LPTSTR) &lpMsgBuf,
    624 						0,
    625 						NULL 
    626 						);
    627 		LocalFree( lpMsgBuf );
    628 
    629 		// Couldn't retrieve symbol (no debug info?, can't load dbghelp.dll?)
    630 		sprintf( funcName, "0x%08x", addr );
    631 		module = "";
    632     }
    633 }
    634 
    635 #else
    636 
    637 /*
    638 ==================
    639 Sym_Init
    640 ==================
    641 */
    642 void Sym_Init( long addr ) {
    643 }
    644 
    645 /*
    646 ==================
    647 Sym_Shutdown
    648 ==================
    649 */
    650 void Sym_Shutdown() {
    651 }
    652 
    653 /*
    654 ==================
    655 Sym_GetFuncInfo
    656 ==================
    657 */
    658 void Sym_GetFuncInfo( long addr, idStr &module, idStr &funcName ) {
    659 	module = "";
    660 	sprintf( funcName, "0x%08x", addr );
    661 }
    662 
    663 #endif
    664 
    665 /*
    666 ==================
    667 GetFuncAddr
    668 ==================
    669 */
    670 address_t GetFuncAddr( address_t midPtPtr ) {
    671 	long temp;
    672 	do {
    673 		temp = (long)(*(long*)midPtPtr);
    674 		if ( (temp&0x00FFFFFF) == PROLOGUE_SIGNATURE ) {
    675 			break;
    676 		}
    677 		midPtPtr--;
    678 	} while(true);
    679 
    680 	return midPtPtr;
    681 }
    682 
    683 /*
    684 ==================
    685 GetCallerAddr
    686 ==================
    687 */
    688 address_t GetCallerAddr( long _ebp ) {
    689 	long midPtPtr;
    690 	long res = 0;
    691 
    692 	__asm {
    693 		mov		eax, _ebp
    694 		mov		ecx, [eax]		// check for end of stack frames list
    695 		test	ecx, ecx		// check for zero stack frame
    696 		jz		label
    697 		mov		eax, [eax+4]	// get the ret address
    698 		test	eax, eax		// check for zero return address
    699 		jz		label
    700 		mov		midPtPtr, eax
    701 	}
    702 	res = GetFuncAddr( midPtPtr );
    703 label:
    704 	return res;
    705 }
    706 
    707 /*
    708 ==================
    709 Sys_GetCallStack
    710 
    711  use /Oy option
    712 ==================
    713 */
    714 void Sys_GetCallStack( address_t *callStack, const int callStackSize ) {
    715 #if 1 //def _DEBUG
    716 	int i;
    717 	long m_ebp;
    718 
    719 	__asm {
    720 		mov eax, ebp
    721 		mov m_ebp, eax
    722 	}
    723 	// skip last two functions
    724 	m_ebp = *((long*)m_ebp);
    725 	m_ebp = *((long*)m_ebp);
    726 	// list functions
    727 	for ( i = 0; i < callStackSize; i++ ) {
    728 		callStack[i] = GetCallerAddr( m_ebp );
    729 		if ( callStack[i] == 0 ) {
    730 			break;
    731 		}
    732 		m_ebp = *((long*)m_ebp);
    733 	}
    734 #else
    735 	int i = 0;
    736 #endif
    737 	while( i < callStackSize ) {
    738 		callStack[i++] = 0;
    739 	}
    740 }
    741 
    742 /*
    743 ==================
    744 Sys_GetCallStackStr
    745 ==================
    746 */
    747 const char *Sys_GetCallStackStr( const address_t *callStack, const int callStackSize ) {
    748 	static char string[MAX_STRING_CHARS*2];
    749 	int index, i;
    750 	idStr module, funcName;
    751 
    752 	index = 0;
    753 	for ( i = callStackSize-1; i >= 0; i-- ) {
    754 		Sym_GetFuncInfo( callStack[i], module, funcName );
    755 		index += sprintf( string+index, " -> %s", funcName.c_str() );
    756 	}
    757 	return string;
    758 }
    759 
    760 /*
    761 ==================
    762 Sys_GetCallStackCurStr
    763 ==================
    764 */
    765 const char *Sys_GetCallStackCurStr( int depth ) {
    766 	address_t *callStack;
    767 
    768 	callStack = (address_t *) _alloca( depth * sizeof( address_t ) );
    769 	Sys_GetCallStack( callStack, depth );
    770 	return Sys_GetCallStackStr( callStack, depth );
    771 }
    772 
    773 /*
    774 ==================
    775 Sys_GetCallStackCurAddressStr
    776 ==================
    777 */
    778 const char *Sys_GetCallStackCurAddressStr( int depth ) {
    779 	static char string[MAX_STRING_CHARS*2];
    780 	address_t *callStack;
    781 	int index, i;
    782 
    783 	callStack = (address_t *) _alloca( depth * sizeof( address_t ) );
    784 	Sys_GetCallStack( callStack, depth );
    785 
    786 	index = 0;
    787 	for ( i = depth-1; i >= 0; i-- ) {
    788 		index += sprintf( string+index, " -> 0x%08x", callStack[i] );
    789 	}
    790 	return string;
    791 }
    792 
    793 /*
    794 ==================
    795 Sys_ShutdownSymbols
    796 ==================
    797 */
    798 void Sys_ShutdownSymbols() {
    799 	Sym_Shutdown();
    800 }