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 }