win_main.cpp 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include "../../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include <errno.h>
  23. #include <float.h>
  24. #include <fcntl.h>
  25. #include <direct.h>
  26. #include <io.h>
  27. #include <conio.h>
  28. #include <mapi.h>
  29. #include <ShellAPI.h>
  30. #ifndef __MRC__
  31. #include <sys/types.h>
  32. #include <sys/stat.h>
  33. #endif
  34. #include "../sys_local.h"
  35. #include "win_local.h"
  36. #include "rc/CreateResourceIDs.h"
  37. #include "../../renderer/tr_local.h"
  38. idCVar Win32Vars_t::sys_arch( "sys_arch", "", CVAR_SYSTEM | CVAR_INIT, "" );
  39. idCVar Win32Vars_t::sys_cpustring( "sys_cpustring", "detect", CVAR_SYSTEM | CVAR_INIT, "" );
  40. idCVar Win32Vars_t::in_mouse( "in_mouse", "1", CVAR_SYSTEM | CVAR_BOOL, "enable mouse input" );
  41. idCVar Win32Vars_t::win_allowAltTab( "win_allowAltTab", "0", CVAR_SYSTEM | CVAR_BOOL, "allow Alt-Tab when fullscreen" );
  42. idCVar Win32Vars_t::win_notaskkeys( "win_notaskkeys", "0", CVAR_SYSTEM | CVAR_INTEGER, "disable windows task keys" );
  43. idCVar Win32Vars_t::win_username( "win_username", "", CVAR_SYSTEM | CVAR_INIT, "windows user name" );
  44. idCVar Win32Vars_t::win_xpos( "win_xpos", "3", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "horizontal position of window" );
  45. idCVar Win32Vars_t::win_ypos( "win_ypos", "22", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "vertical position of window" );
  46. idCVar Win32Vars_t::win_outputDebugString( "win_outputDebugString", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
  47. idCVar Win32Vars_t::win_outputEditString( "win_outputEditString", "1", CVAR_SYSTEM | CVAR_BOOL, "" );
  48. idCVar Win32Vars_t::win_viewlog( "win_viewlog", "0", CVAR_SYSTEM | CVAR_INTEGER, "" );
  49. idCVar Win32Vars_t::win_timerUpdate( "win_timerUpdate", "0", CVAR_SYSTEM | CVAR_BOOL, "allows the game to be updated while dragging the window" );
  50. idCVar Win32Vars_t::win_allowMultipleInstances( "win_allowMultipleInstances", "0", CVAR_SYSTEM | CVAR_BOOL, "allow multiple instances running concurrently" );
  51. Win32Vars_t win32;
  52. static char sys_cmdline[MAX_STRING_CHARS];
  53. // not a hard limit, just what we keep track of for debugging
  54. xthreadInfo *g_threads[MAX_THREADS];
  55. int g_thread_count = 0;
  56. static sysMemoryStats_t exeLaunchMemoryStats;
  57. static xthreadInfo threadInfo;
  58. static HANDLE hTimer;
  59. /*
  60. ================
  61. Sys_GetExeLaunchMemoryStatus
  62. ================
  63. */
  64. void Sys_GetExeLaunchMemoryStatus( sysMemoryStats_t &stats ) {
  65. stats = exeLaunchMemoryStats;
  66. }
  67. /*
  68. ==================
  69. Sys_Createthread
  70. ==================
  71. */
  72. void Sys_CreateThread( xthread_t function, void *parms, xthreadPriority priority, xthreadInfo &info, const char *name, xthreadInfo *threads[MAX_THREADS], int *thread_count ) {
  73. HANDLE temp = CreateThread( NULL, // LPSECURITY_ATTRIBUTES lpsa,
  74. 0, // DWORD cbStack,
  75. (LPTHREAD_START_ROUTINE)function, // LPTHREAD_START_ROUTINE lpStartAddr,
  76. parms, // LPVOID lpvThreadParm,
  77. 0, // DWORD fdwCreate,
  78. &info.threadId);
  79. info.threadHandle = (int) temp;
  80. if (priority == THREAD_HIGHEST) {
  81. SetThreadPriority( (HANDLE)info.threadHandle, THREAD_PRIORITY_HIGHEST ); // we better sleep enough to do this
  82. } else if (priority == THREAD_ABOVE_NORMAL ) {
  83. SetThreadPriority( (HANDLE)info.threadHandle, THREAD_PRIORITY_ABOVE_NORMAL );
  84. }
  85. info.name = name;
  86. if ( *thread_count < MAX_THREADS ) {
  87. threads[(*thread_count)++] = &info;
  88. } else {
  89. common->DPrintf("WARNING: MAX_THREADS reached\n");
  90. }
  91. }
  92. /*
  93. ==================
  94. Sys_DestroyThread
  95. ==================
  96. */
  97. void Sys_DestroyThread( xthreadInfo& info ) {
  98. WaitForSingleObject( (HANDLE)info.threadHandle, INFINITE);
  99. CloseHandle( (HANDLE)info.threadHandle );
  100. info.threadHandle = 0;
  101. }
  102. /*
  103. ==================
  104. Sys_Sentry
  105. ==================
  106. */
  107. void Sys_Sentry() {
  108. int j = 0;
  109. }
  110. /*
  111. ==================
  112. Sys_GetThreadName
  113. ==================
  114. */
  115. const char* Sys_GetThreadName(int *index) {
  116. int id = GetCurrentThreadId();
  117. for( int i = 0; i < g_thread_count; i++ ) {
  118. if ( id == g_threads[i]->threadId ) {
  119. if ( index ) {
  120. *index = i;
  121. }
  122. return g_threads[i]->name;
  123. }
  124. }
  125. if ( index ) {
  126. *index = -1;
  127. }
  128. return "main";
  129. }
  130. /*
  131. ==================
  132. Sys_EnterCriticalSection
  133. ==================
  134. */
  135. void Sys_EnterCriticalSection( int index ) {
  136. assert( index >= 0 && index < MAX_CRITICAL_SECTIONS );
  137. if ( TryEnterCriticalSection( &win32.criticalSections[index] ) == 0 ) {
  138. EnterCriticalSection( &win32.criticalSections[index] );
  139. // Sys_DebugPrintf( "busy lock '%s' in thread '%s'\n", lock->name, Sys_GetThreadName() );
  140. }
  141. }
  142. /*
  143. ==================
  144. Sys_LeaveCriticalSection
  145. ==================
  146. */
  147. void Sys_LeaveCriticalSection( int index ) {
  148. assert( index >= 0 && index < MAX_CRITICAL_SECTIONS );
  149. LeaveCriticalSection( &win32.criticalSections[index] );
  150. }
  151. /*
  152. ==================
  153. Sys_WaitForEvent
  154. ==================
  155. */
  156. void Sys_WaitForEvent( int index ) {
  157. assert( index == 0 );
  158. if ( !win32.backgroundDownloadSemaphore ) {
  159. win32.backgroundDownloadSemaphore = CreateEvent( NULL, TRUE, FALSE, NULL );
  160. }
  161. WaitForSingleObject( win32.backgroundDownloadSemaphore, INFINITE );
  162. ResetEvent( win32.backgroundDownloadSemaphore );
  163. }
  164. /*
  165. ==================
  166. Sys_TriggerEvent
  167. ==================
  168. */
  169. void Sys_TriggerEvent( int index ) {
  170. assert( index == 0 );
  171. SetEvent( win32.backgroundDownloadSemaphore );
  172. }
  173. #pragma optimize( "", on )
  174. #ifdef DEBUG
  175. static unsigned int debug_total_alloc = 0;
  176. static unsigned int debug_total_alloc_count = 0;
  177. static unsigned int debug_current_alloc = 0;
  178. static unsigned int debug_current_alloc_count = 0;
  179. static unsigned int debug_frame_alloc = 0;
  180. static unsigned int debug_frame_alloc_count = 0;
  181. idCVar sys_showMallocs( "sys_showMallocs", "0", CVAR_SYSTEM, "" );
  182. // _HOOK_ALLOC, _HOOK_REALLOC, _HOOK_FREE
  183. typedef struct CrtMemBlockHeader
  184. {
  185. struct _CrtMemBlockHeader *pBlockHeaderNext; // Pointer to the block allocated just before this one:
  186. struct _CrtMemBlockHeader *pBlockHeaderPrev; // Pointer to the block allocated just after this one
  187. char *szFileName; // File name
  188. int nLine; // Line number
  189. size_t nDataSize; // Size of user block
  190. int nBlockUse; // Type of block
  191. long lRequest; // Allocation number
  192. byte gap[4]; // Buffer just before (lower than) the user's memory:
  193. } CrtMemBlockHeader;
  194. #include <crtdbg.h>
  195. /*
  196. ==================
  197. Sys_AllocHook
  198. called for every malloc/new/free/delete
  199. ==================
  200. */
  201. int Sys_AllocHook( int nAllocType, void *pvData, size_t nSize, int nBlockUse, long lRequest, const unsigned char * szFileName, int nLine )
  202. {
  203. CrtMemBlockHeader *pHead;
  204. byte *temp;
  205. if ( nBlockUse == _CRT_BLOCK )
  206. {
  207. return( TRUE );
  208. }
  209. // get a pointer to memory block header
  210. temp = ( byte * )pvData;
  211. temp -= 32;
  212. pHead = ( CrtMemBlockHeader * )temp;
  213. switch( nAllocType ) {
  214. case _HOOK_ALLOC:
  215. debug_total_alloc += nSize;
  216. debug_current_alloc += nSize;
  217. debug_frame_alloc += nSize;
  218. debug_total_alloc_count++;
  219. debug_current_alloc_count++;
  220. debug_frame_alloc_count++;
  221. break;
  222. case _HOOK_FREE:
  223. assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd );
  224. debug_current_alloc -= pHead->nDataSize;
  225. debug_current_alloc_count--;
  226. debug_total_alloc_count++;
  227. debug_frame_alloc_count++;
  228. break;
  229. case _HOOK_REALLOC:
  230. assert( pHead->gap[0] == 0xfd && pHead->gap[1] == 0xfd && pHead->gap[2] == 0xfd && pHead->gap[3] == 0xfd );
  231. debug_current_alloc -= pHead->nDataSize;
  232. debug_total_alloc += nSize;
  233. debug_current_alloc += nSize;
  234. debug_frame_alloc += nSize;
  235. debug_total_alloc_count++;
  236. debug_current_alloc_count--;
  237. debug_frame_alloc_count++;
  238. break;
  239. }
  240. return( TRUE );
  241. }
  242. /*
  243. ==================
  244. Sys_DebugMemory_f
  245. ==================
  246. */
  247. void Sys_DebugMemory_f( void ) {
  248. common->Printf( "Total allocation %8dk in %d blocks\n", debug_total_alloc / 1024, debug_total_alloc_count );
  249. common->Printf( "Current allocation %8dk in %d blocks\n", debug_current_alloc / 1024, debug_current_alloc_count );
  250. }
  251. /*
  252. ==================
  253. Sys_MemFrame
  254. ==================
  255. */
  256. void Sys_MemFrame( void ) {
  257. if( sys_showMallocs.GetInteger() ) {
  258. common->Printf("Frame: %8dk in %5d blocks\n", debug_frame_alloc / 1024, debug_frame_alloc_count );
  259. }
  260. debug_frame_alloc = 0;
  261. debug_frame_alloc_count = 0;
  262. }
  263. #endif
  264. /*
  265. ==================
  266. Sys_FlushCacheMemory
  267. On windows, the vertex buffers are write combined, so they
  268. don't need to be flushed from the cache
  269. ==================
  270. */
  271. void Sys_FlushCacheMemory( void *base, int bytes ) {
  272. }
  273. /*
  274. =============
  275. Sys_Error
  276. Show the early console as an error dialog
  277. =============
  278. */
  279. void Sys_Error( const char *error, ... ) {
  280. va_list argptr;
  281. char text[4096];
  282. MSG msg;
  283. va_start( argptr, error );
  284. vsprintf( text, error, argptr );
  285. va_end( argptr);
  286. Conbuf_AppendText( text );
  287. Conbuf_AppendText( "\n" );
  288. Win_SetErrorText( text );
  289. Sys_ShowConsole( 1, true );
  290. timeEndPeriod( 1 );
  291. Sys_ShutdownInput();
  292. GLimp_Shutdown();
  293. // wait for the user to quit
  294. while ( 1 ) {
  295. if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
  296. common->Quit();
  297. }
  298. TranslateMessage( &msg );
  299. DispatchMessage( &msg );
  300. }
  301. Sys_DestroyConsole();
  302. exit (1);
  303. }
  304. /*
  305. ==============
  306. Sys_Quit
  307. ==============
  308. */
  309. void Sys_Quit( void ) {
  310. timeEndPeriod( 1 );
  311. Sys_ShutdownInput();
  312. Sys_DestroyConsole();
  313. ExitProcess( 0 );
  314. }
  315. /*
  316. ==============
  317. Sys_Printf
  318. ==============
  319. */
  320. #define MAXPRINTMSG 4096
  321. void Sys_Printf( const char *fmt, ... ) {
  322. char msg[MAXPRINTMSG];
  323. va_list argptr;
  324. va_start(argptr, fmt);
  325. idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
  326. va_end(argptr);
  327. msg[sizeof(msg)-1] = '\0';
  328. if ( win32.win_outputDebugString.GetBool() ) {
  329. OutputDebugString( msg );
  330. }
  331. if ( win32.win_outputEditString.GetBool() ) {
  332. Conbuf_AppendText( msg );
  333. }
  334. }
  335. /*
  336. ==============
  337. Sys_DebugPrintf
  338. ==============
  339. */
  340. #define MAXPRINTMSG 4096
  341. void Sys_DebugPrintf( const char *fmt, ... ) {
  342. char msg[MAXPRINTMSG];
  343. va_list argptr;
  344. va_start( argptr, fmt );
  345. idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, argptr );
  346. msg[ sizeof(msg)-1 ] = '\0';
  347. va_end( argptr );
  348. OutputDebugString( msg );
  349. }
  350. /*
  351. ==============
  352. Sys_DebugVPrintf
  353. ==============
  354. */
  355. void Sys_DebugVPrintf( const char *fmt, va_list arg ) {
  356. char msg[MAXPRINTMSG];
  357. idStr::vsnPrintf( msg, MAXPRINTMSG-1, fmt, arg );
  358. msg[ sizeof(msg)-1 ] = '\0';
  359. OutputDebugString( msg );
  360. }
  361. /*
  362. ==============
  363. Sys_Sleep
  364. ==============
  365. */
  366. void Sys_Sleep( int msec ) {
  367. Sleep( msec );
  368. }
  369. /*
  370. ==============
  371. Sys_ShowWindow
  372. ==============
  373. */
  374. void Sys_ShowWindow( bool show ) {
  375. ::ShowWindow( win32.hWnd, show ? SW_SHOW : SW_HIDE );
  376. }
  377. /*
  378. ==============
  379. Sys_IsWindowVisible
  380. ==============
  381. */
  382. bool Sys_IsWindowVisible( void ) {
  383. return ( ::IsWindowVisible( win32.hWnd ) != 0 );
  384. }
  385. /*
  386. ==============
  387. Sys_Mkdir
  388. ==============
  389. */
  390. void Sys_Mkdir( const char *path ) {
  391. _mkdir (path);
  392. }
  393. /*
  394. =================
  395. Sys_FileTimeStamp
  396. =================
  397. */
  398. ID_TIME_T Sys_FileTimeStamp( FILE *fp ) {
  399. struct _stat st;
  400. _fstat( _fileno( fp ), &st );
  401. return (long) st.st_mtime;
  402. }
  403. /*
  404. ==============
  405. Sys_Cwd
  406. ==============
  407. */
  408. const char *Sys_Cwd( void ) {
  409. static char cwd[MAX_OSPATH];
  410. _getcwd( cwd, sizeof( cwd ) - 1 );
  411. cwd[MAX_OSPATH-1] = 0;
  412. return cwd;
  413. }
  414. /*
  415. ==============
  416. Sys_DefaultCDPath
  417. ==============
  418. */
  419. const char *Sys_DefaultCDPath( void ) {
  420. return "";
  421. }
  422. /*
  423. ==============
  424. Sys_DefaultBasePath
  425. ==============
  426. */
  427. const char *Sys_DefaultBasePath( void ) {
  428. return Sys_Cwd();
  429. }
  430. /*
  431. ==============
  432. Sys_DefaultSavePath
  433. ==============
  434. */
  435. const char *Sys_DefaultSavePath( void ) {
  436. return cvarSystem->GetCVarString( "fs_basepath" );
  437. }
  438. /*
  439. ==============
  440. Sys_EXEPath
  441. ==============
  442. */
  443. const char *Sys_EXEPath( void ) {
  444. static char exe[ MAX_OSPATH ];
  445. GetModuleFileName( NULL, exe, sizeof( exe ) - 1 );
  446. return exe;
  447. }
  448. /*
  449. ==============
  450. Sys_ListFiles
  451. ==============
  452. */
  453. int Sys_ListFiles( const char *directory, const char *extension, idStrList &list ) {
  454. idStr search;
  455. struct _finddata_t findinfo;
  456. int findhandle;
  457. int flag;
  458. if ( !extension) {
  459. extension = "";
  460. }
  461. // passing a slash as extension will find directories
  462. if ( extension[0] == '/' && extension[1] == 0 ) {
  463. extension = "";
  464. flag = 0;
  465. } else {
  466. flag = _A_SUBDIR;
  467. }
  468. sprintf( search, "%s\\*%s", directory, extension );
  469. // search
  470. list.Clear();
  471. findhandle = _findfirst( search, &findinfo );
  472. if ( findhandle == -1 ) {
  473. return -1;
  474. }
  475. do {
  476. if ( flag ^ ( findinfo.attrib & _A_SUBDIR ) ) {
  477. list.Append( findinfo.name );
  478. }
  479. } while ( _findnext( findhandle, &findinfo ) != -1 );
  480. _findclose( findhandle );
  481. return list.Num();
  482. }
  483. /*
  484. ================
  485. Sys_GetClipboardData
  486. ================
  487. */
  488. char *Sys_GetClipboardData( void ) {
  489. char *data = NULL;
  490. char *cliptext;
  491. if ( OpenClipboard( NULL ) != 0 ) {
  492. HANDLE hClipboardData;
  493. if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) {
  494. if ( ( cliptext = (char *)GlobalLock( hClipboardData ) ) != 0 ) {
  495. data = (char *)Mem_Alloc( GlobalSize( hClipboardData ) + 1 );
  496. strcpy( data, cliptext );
  497. GlobalUnlock( hClipboardData );
  498. strtok( data, "\n\r\b" );
  499. }
  500. }
  501. CloseClipboard();
  502. }
  503. return data;
  504. }
  505. /*
  506. ================
  507. Sys_SetClipboardData
  508. ================
  509. */
  510. void Sys_SetClipboardData( const char *string ) {
  511. HGLOBAL HMem;
  512. char *PMem;
  513. // allocate memory block
  514. HMem = (char *)::GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, strlen( string ) + 1 );
  515. if ( HMem == NULL ) {
  516. return;
  517. }
  518. // lock allocated memory and obtain a pointer
  519. PMem = (char *)::GlobalLock( HMem );
  520. if ( PMem == NULL ) {
  521. return;
  522. }
  523. // copy text into allocated memory block
  524. lstrcpy( PMem, string );
  525. // unlock allocated memory
  526. ::GlobalUnlock( HMem );
  527. // open Clipboard
  528. if ( !OpenClipboard( 0 ) ) {
  529. ::GlobalFree( HMem );
  530. return;
  531. }
  532. // remove current Clipboard contents
  533. EmptyClipboard();
  534. // supply the memory handle to the Clipboard
  535. SetClipboardData( CF_TEXT, HMem );
  536. HMem = 0;
  537. // close Clipboard
  538. CloseClipboard();
  539. }
  540. /*
  541. ========================================================================
  542. DLL Loading
  543. ========================================================================
  544. */
  545. /*
  546. =====================
  547. Sys_DLL_Load
  548. =====================
  549. */
  550. int Sys_DLL_Load( const char *dllName ) {
  551. HINSTANCE libHandle;
  552. libHandle = LoadLibrary( dllName );
  553. if ( libHandle ) {
  554. // since we can't have LoadLibrary load only from the specified path, check it did the right thing
  555. char loadedPath[ MAX_OSPATH ];
  556. GetModuleFileName( libHandle, loadedPath, sizeof( loadedPath ) - 1 );
  557. if ( idStr::IcmpPath( dllName, loadedPath ) ) {
  558. Sys_Printf( "ERROR: LoadLibrary '%s' wants to load '%s'\n", dllName, loadedPath );
  559. Sys_DLL_Unload( (int)libHandle );
  560. return 0;
  561. }
  562. }
  563. return (int)libHandle;
  564. }
  565. /*
  566. =====================
  567. Sys_DLL_GetProcAddress
  568. =====================
  569. */
  570. void *Sys_DLL_GetProcAddress( int dllHandle, const char *procName ) {
  571. return GetProcAddress( (HINSTANCE)dllHandle, procName );
  572. }
  573. /*
  574. =====================
  575. Sys_DLL_Unload
  576. =====================
  577. */
  578. void Sys_DLL_Unload( int dllHandle ) {
  579. if ( !dllHandle ) {
  580. return;
  581. }
  582. if ( FreeLibrary( (HINSTANCE)dllHandle ) == 0 ) {
  583. int lastError = GetLastError();
  584. LPVOID lpMsgBuf;
  585. FormatMessage(
  586. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  587. NULL,
  588. lastError,
  589. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  590. (LPTSTR) &lpMsgBuf,
  591. 0,
  592. NULL
  593. );
  594. Sys_Error( "Sys_DLL_Unload: FreeLibrary failed - %s (%d)", lpMsgBuf, lastError );
  595. }
  596. }
  597. /*
  598. ========================================================================
  599. EVENT LOOP
  600. ========================================================================
  601. */
  602. #ifndef USE_SDL
  603. #define MAX_QUED_EVENTS 256
  604. #define MASK_QUED_EVENTS ( MAX_QUED_EVENTS - 1 )
  605. sysEvent_t eventQue[MAX_QUED_EVENTS];
  606. int eventHead = 0;
  607. int eventTail = 0;
  608. /*
  609. ================
  610. Sys_QueEvent
  611. Ptr should either be null, or point to a block of data that can
  612. be freed by the game later.
  613. ================
  614. */
  615. void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr ) {
  616. sysEvent_t *ev;
  617. ev = &eventQue[ eventHead & MASK_QUED_EVENTS ];
  618. if ( eventHead - eventTail >= MAX_QUED_EVENTS ) {
  619. common->Printf("Sys_QueEvent: overflow\n");
  620. // we are discarding an event, but don't leak memory
  621. if ( ev->evPtr ) {
  622. Mem_Free( ev->evPtr );
  623. }
  624. eventTail++;
  625. }
  626. eventHead++;
  627. ev->evType = type;
  628. ev->evValue = value;
  629. ev->evValue2 = value2;
  630. ev->evPtrLength = ptrLength;
  631. ev->evPtr = ptr;
  632. }
  633. /*
  634. =============
  635. Sys_PumpEvents
  636. This allows windows to be moved during renderbump
  637. =============
  638. */
  639. void Sys_PumpEvents( void ) {
  640. MSG msg;
  641. // pump the message loop
  642. while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) {
  643. if ( !GetMessage( &msg, NULL, 0, 0 ) ) {
  644. common->Quit();
  645. }
  646. // save the msg time, because wndprocs don't have access to the timestamp
  647. if ( win32.sysMsgTime && win32.sysMsgTime > (int)msg.time ) {
  648. // don't ever let the event times run backwards
  649. // common->Printf( "Sys_PumpEvents: win32.sysMsgTime (%i) > msg.time (%i)\n", win32.sysMsgTime, msg.time );
  650. } else {
  651. win32.sysMsgTime = msg.time;
  652. }
  653. #ifdef ID_ALLOW_TOOLS
  654. if ( GUIEditorHandleMessage ( &msg ) ) {
  655. continue;
  656. }
  657. #endif
  658. TranslateMessage (&msg);
  659. DispatchMessage (&msg);
  660. }
  661. }
  662. /*
  663. ================
  664. Sys_GenerateEvents
  665. ================
  666. */
  667. void Sys_GenerateEvents( void ) {
  668. static int entered = false;
  669. char *s;
  670. if ( entered ) {
  671. return;
  672. }
  673. entered = true;
  674. // pump the message loop
  675. Sys_PumpEvents();
  676. // make sure mouse and joystick are only called once a frame
  677. IN_Frame();
  678. // check for console commands
  679. s = Sys_ConsoleInput();
  680. if ( s ) {
  681. char *b;
  682. int len;
  683. len = strlen( s ) + 1;
  684. b = (char *)Mem_Alloc( len );
  685. strcpy( b, s );
  686. Sys_QueEvent( 0, SE_CONSOLE, 0, 0, len, b );
  687. }
  688. entered = false;
  689. }
  690. /*
  691. ================
  692. Sys_ClearEvents
  693. ================
  694. */
  695. void Sys_ClearEvents( void ) {
  696. eventHead = eventTail = 0;
  697. }
  698. /*
  699. ================
  700. Sys_GetEvent
  701. ================
  702. */
  703. sysEvent_t Sys_GetEvent( void ) {
  704. sysEvent_t ev;
  705. // return if we have data
  706. if ( eventHead > eventTail ) {
  707. eventTail++;
  708. return eventQue[ ( eventTail - 1 ) & MASK_QUED_EVENTS ];
  709. }
  710. // return the empty event
  711. memset( &ev, 0, sizeof( ev ) );
  712. return ev;
  713. }
  714. #endif
  715. //================================================================
  716. /*
  717. =================
  718. Sys_In_Restart_f
  719. Restart the input subsystem
  720. =================
  721. */
  722. void Sys_In_Restart_f( const idCmdArgs &args ) {
  723. Sys_ShutdownInput();
  724. Sys_InitInput();
  725. }
  726. /*
  727. ==================
  728. Sys_AsyncThread
  729. ==================
  730. */
  731. static void Sys_AsyncThread( void *parm ) {
  732. #if ENABLE_FP_EXCEPTIONS
  733. FPExceptionEnabler enabled(_EM_UNDERFLOW | _EM_OVERFLOW | _EM_ZERODIVIDE | _EM_INVALID);
  734. #endif
  735. int wakeNumber;
  736. int startTime;
  737. startTime = Sys_Milliseconds();
  738. wakeNumber = 0;
  739. while ( 1 ) {
  740. #ifdef WIN32
  741. // this will trigger 60 times a second
  742. int r = WaitForSingleObject( hTimer, 100 );
  743. if ( r != WAIT_OBJECT_0 ) {
  744. OutputDebugString( "idPacketServer::PacketServerInterrupt: bad wait return" );
  745. }
  746. #endif
  747. #if 0
  748. wakeNumber++;
  749. int msec = Sys_Milliseconds();
  750. int deltaTime = msec - startTime;
  751. startTime = msec;
  752. char str[1024];
  753. sprintf( str, "%i ", deltaTime );
  754. OutputDebugString( str );
  755. #endif
  756. common->Async();
  757. }
  758. }
  759. /*
  760. ==============
  761. Sys_StartAsyncThread
  762. Start the thread that will call idCommon::Async()
  763. ==============
  764. */
  765. void Sys_StartAsyncThread( void ) {
  766. // create an auto-reset event that happens 60 times a second
  767. hTimer = CreateWaitableTimer( NULL, false, NULL );
  768. if ( !hTimer ) {
  769. common->Error( "idPacketServer::Spawn: CreateWaitableTimer failed" );
  770. }
  771. LARGE_INTEGER t;
  772. t.HighPart = t.LowPart = 0;
  773. SetWaitableTimer( hTimer, &t, USERCMD_MSEC, NULL, NULL, TRUE );
  774. Sys_CreateThread( (xthread_t)Sys_AsyncThread, NULL, THREAD_ABOVE_NORMAL, threadInfo, "Async", g_threads, &g_thread_count );
  775. #ifdef SET_THREAD_AFFINITY
  776. // give the async thread an affinity for the second cpu
  777. SetThreadAffinityMask( (HANDLE)threadInfo.threadHandle, 2 );
  778. #endif
  779. if ( !threadInfo.threadHandle ) {
  780. common->Error( "Sys_StartAsyncThread: failed" );
  781. }
  782. }
  783. /*
  784. ================
  785. Sys_AlreadyRunning
  786. returns true if there is a copy of D3 running already
  787. ================
  788. */
  789. bool Sys_AlreadyRunning( void ) {
  790. #ifndef DEBUG
  791. if ( !win32.win_allowMultipleInstances.GetBool() ) {
  792. HANDLE hMutexOneInstance = ::CreateMutex( NULL, FALSE, "DOOM3" );
  793. if ( ::GetLastError() == ERROR_ALREADY_EXISTS || ::GetLastError() == ERROR_ACCESS_DENIED ) {
  794. return true;
  795. }
  796. }
  797. #endif
  798. return false;
  799. }
  800. /*
  801. ================
  802. Sys_Init
  803. The cvar system must already be setup
  804. ================
  805. */
  806. #define OSR2_BUILD_NUMBER 1111
  807. #define WIN98_BUILD_NUMBER 1998
  808. void Sys_Init( void ) {
  809. CoInitialize( NULL );
  810. // make sure the timer is high precision, otherwise
  811. // NT gets 18ms resolution
  812. timeBeginPeriod( 1 );
  813. // get WM_TIMER messages pumped every millisecond
  814. // SetTimer( NULL, 0, 100, NULL );
  815. cmdSystem->AddCommand( "in_restart", Sys_In_Restart_f, CMD_FL_SYSTEM, "restarts the input system" );
  816. #ifdef DEBUG
  817. cmdSystem->AddCommand( "createResourceIDs", CreateResourceIDs_f, CMD_FL_TOOL, "assigns resource IDs in _resouce.h files" );
  818. #endif
  819. #if 0
  820. cmdSystem->AddCommand( "setAsyncSound", Sys_SetAsyncSound_f, CMD_FL_SYSTEM, "set the async sound option" );
  821. #endif
  822. //
  823. // Windows user name
  824. //
  825. win32.win_username.SetString( Sys_GetCurrentUser() );
  826. //
  827. // Windows version
  828. //
  829. win32.osversion.dwOSVersionInfoSize = sizeof( win32.osversion );
  830. if ( !GetVersionEx( (LPOSVERSIONINFO)&win32.osversion ) )
  831. Sys_Error( "Couldn't get OS info" );
  832. if ( win32.osversion.dwMajorVersion < 4 ) {
  833. Sys_Error( GAME_NAME " requires Windows version 4 (NT) or greater" );
  834. }
  835. if ( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32s ) {
  836. Sys_Error( GAME_NAME " doesn't run on Win32s" );
  837. }
  838. if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_NT ) {
  839. if( win32.osversion.dwMajorVersion <= 4 ) {
  840. win32.sys_arch.SetString( "WinNT (NT)" );
  841. } else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 0 ) {
  842. win32.sys_arch.SetString( "Win2K (NT)" );
  843. } else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 1 ) {
  844. win32.sys_arch.SetString( "WinXP (NT)" );
  845. } else if ( win32.osversion.dwMajorVersion == 6 ) {
  846. win32.sys_arch.SetString( "Vista" );
  847. } else {
  848. win32.sys_arch.SetString( "Unknown NT variant" );
  849. }
  850. } else if( win32.osversion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) {
  851. if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 0 ) {
  852. // Win95
  853. if( win32.osversion.szCSDVersion[1] == 'C' ) {
  854. win32.sys_arch.SetString( "Win95 OSR2 (95)" );
  855. } else {
  856. win32.sys_arch.SetString( "Win95 (95)" );
  857. }
  858. } else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 10 ) {
  859. // Win98
  860. if( win32.osversion.szCSDVersion[1] == 'A' ) {
  861. win32.sys_arch.SetString( "Win98SE (95)" );
  862. } else {
  863. win32.sys_arch.SetString( "Win98 (95)" );
  864. }
  865. } else if( win32.osversion.dwMajorVersion == 4 && win32.osversion.dwMinorVersion == 90 ) {
  866. // WinMe
  867. win32.sys_arch.SetString( "WinMe (95)" );
  868. } else {
  869. win32.sys_arch.SetString( "Unknown 95 variant" );
  870. }
  871. } else {
  872. win32.sys_arch.SetString( "unknown Windows variant" );
  873. }
  874. //
  875. // CPU type
  876. //
  877. if ( !idStr::Icmp( win32.sys_cpustring.GetString(), "detect" ) ) {
  878. idStr string;
  879. common->Printf( "%1.0f MHz ", Sys_ClockTicksPerSecond() / 1000000.0f );
  880. win32.cpuid = Sys_GetCPUId();
  881. string.Clear();
  882. if ( win32.cpuid & CPUID_AMD ) {
  883. string += "AMD CPU";
  884. } else if ( win32.cpuid & CPUID_INTEL ) {
  885. string += "Intel CPU";
  886. } else if ( win32.cpuid & CPUID_UNSUPPORTED ) {
  887. string += "unsupported CPU";
  888. } else {
  889. string += "generic CPU";
  890. }
  891. string += " with ";
  892. if ( win32.cpuid & CPUID_MMX ) {
  893. string += "MMX & ";
  894. }
  895. if ( win32.cpuid & CPUID_3DNOW ) {
  896. string += "3DNow! & ";
  897. }
  898. if ( win32.cpuid & CPUID_SSE ) {
  899. string += "SSE & ";
  900. }
  901. if ( win32.cpuid & CPUID_SSE2 ) {
  902. string += "SSE2 & ";
  903. }
  904. if ( win32.cpuid & CPUID_SSE3 ) {
  905. string += "SSE3 & ";
  906. }
  907. if ( win32.cpuid & CPUID_HTT ) {
  908. string += "HTT & ";
  909. }
  910. string.StripTrailing( " & " );
  911. string.StripTrailing( " with " );
  912. win32.sys_cpustring.SetString( string );
  913. } else {
  914. common->Printf( "forcing CPU type to " );
  915. idLexer src( win32.sys_cpustring.GetString(), idStr::Length( win32.sys_cpustring.GetString() ), "sys_cpustring" );
  916. idToken token;
  917. int id = CPUID_NONE;
  918. while( src.ReadToken( &token ) ) {
  919. if ( token.Icmp( "generic" ) == 0 ) {
  920. id |= CPUID_GENERIC;
  921. } else if ( token.Icmp( "intel" ) == 0 ) {
  922. id |= CPUID_INTEL;
  923. } else if ( token.Icmp( "amd" ) == 0 ) {
  924. id |= CPUID_AMD;
  925. } else if ( token.Icmp( "mmx" ) == 0 ) {
  926. id |= CPUID_MMX;
  927. } else if ( token.Icmp( "3dnow" ) == 0 ) {
  928. id |= CPUID_3DNOW;
  929. } else if ( token.Icmp( "sse" ) == 0 ) {
  930. id |= CPUID_SSE;
  931. } else if ( token.Icmp( "sse2" ) == 0 ) {
  932. id |= CPUID_SSE2;
  933. } else if ( token.Icmp( "sse3" ) == 0 ) {
  934. id |= CPUID_SSE3;
  935. } else if ( token.Icmp( "htt" ) == 0 ) {
  936. id |= CPUID_HTT;
  937. }
  938. }
  939. if ( id == CPUID_NONE ) {
  940. common->Printf( "WARNING: unknown sys_cpustring '%s'\n", win32.sys_cpustring.GetString() );
  941. id = CPUID_GENERIC;
  942. }
  943. win32.cpuid = (cpuid_t) id;
  944. }
  945. common->Printf( "%s\n", win32.sys_cpustring.GetString() );
  946. common->Printf( "%d MB System Memory\n", Sys_GetSystemRam() );
  947. common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
  948. }
  949. /*
  950. ================
  951. Sys_Shutdown
  952. ================
  953. */
  954. void Sys_Shutdown( void ) {
  955. CoUninitialize();
  956. }
  957. /*
  958. ================
  959. Sys_GetProcessorId
  960. ================
  961. */
  962. cpuid_t Sys_GetProcessorId( void ) {
  963. return win32.cpuid;
  964. }
  965. /*
  966. ================
  967. Sys_GetProcessorString
  968. ================
  969. */
  970. const char *Sys_GetProcessorString( void ) {
  971. return win32.sys_cpustring.GetString();
  972. }
  973. //=======================================================================
  974. //#define SET_THREAD_AFFINITY
  975. /*
  976. ====================
  977. Win_Frame
  978. ====================
  979. */
  980. void Win_Frame( void ) {
  981. // if "viewlog" has been modified, show or hide the log console
  982. if ( win32.win_viewlog.IsModified() ) {
  983. if ( !com_skipRenderer.GetBool() && idAsyncNetwork::serverDedicated.GetInteger() != 1 ) {
  984. Sys_ShowConsole( win32.win_viewlog.GetInteger(), false );
  985. }
  986. win32.win_viewlog.ClearModified();
  987. }
  988. }
  989. extern "C" { void _chkstk( int size ); };
  990. void clrstk( void );
  991. /*
  992. ====================
  993. TestChkStk
  994. ====================
  995. */
  996. void TestChkStk( void ) {
  997. int buffer[0x1000];
  998. buffer[0] = 1;
  999. }
  1000. /*
  1001. ====================
  1002. HackChkStk
  1003. ====================
  1004. */
  1005. void HackChkStk( void ) {
  1006. DWORD old;
  1007. VirtualProtect( _chkstk, 6, PAGE_EXECUTE_READWRITE, &old );
  1008. *(byte *)_chkstk = 0xe9;
  1009. *(int *)((int)_chkstk+1) = (int)clrstk - (int)_chkstk - 5;
  1010. TestChkStk();
  1011. }
  1012. /*
  1013. ====================
  1014. GetExceptionCodeInfo
  1015. ====================
  1016. */
  1017. const char *GetExceptionCodeInfo( UINT code ) {
  1018. switch( code ) {
  1019. case EXCEPTION_ACCESS_VIOLATION: return "The thread tried to read from or write to a virtual address for which it does not have the appropriate access.";
  1020. case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.";
  1021. case EXCEPTION_BREAKPOINT: return "A breakpoint was encountered.";
  1022. case EXCEPTION_DATATYPE_MISALIGNMENT: return "The thread tried to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries; 32-bit values on 4-byte boundaries, and so on.";
  1023. case EXCEPTION_FLT_DENORMAL_OPERAND: return "One of the operands in a floating-point operation is denormal. A denormal value is one that is too small to represent as a standard floating-point value.";
  1024. case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "The thread tried to divide a floating-point value by a floating-point divisor of zero.";
  1025. case EXCEPTION_FLT_INEXACT_RESULT: return "The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
  1026. case EXCEPTION_FLT_INVALID_OPERATION: return "This exception represents any floating-point exception not included in this list.";
  1027. case EXCEPTION_FLT_OVERFLOW: return "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
  1028. case EXCEPTION_FLT_STACK_CHECK: return "The stack overflowed or underflowed as the result of a floating-point operation.";
  1029. case EXCEPTION_FLT_UNDERFLOW: return "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
  1030. case EXCEPTION_ILLEGAL_INSTRUCTION: return "The thread tried to execute an invalid instruction.";
  1031. case EXCEPTION_IN_PAGE_ERROR: return "The thread tried to access a page that was not present, and the system was unable to load the page. For example, this exception might occur if a network connection is lost while running a program over the network.";
  1032. case EXCEPTION_INT_DIVIDE_BY_ZERO: return "The thread tried to divide an integer value by an integer divisor of zero.";
  1033. case EXCEPTION_INT_OVERFLOW: return "The result of an integer operation caused a carry out of the most significant bit of the result.";
  1034. case EXCEPTION_INVALID_DISPOSITION: return "An exception handler returned an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception.";
  1035. case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "The thread tried to continue execution after a noncontinuable exception occurred.";
  1036. case EXCEPTION_PRIV_INSTRUCTION: return "The thread tried to execute an instruction whose operation is not allowed in the current machine mode.";
  1037. case EXCEPTION_SINGLE_STEP: return "A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
  1038. case EXCEPTION_STACK_OVERFLOW: return "The thread used up its stack.";
  1039. default: return "Unknown exception";
  1040. }
  1041. }
  1042. /*
  1043. ====================
  1044. EmailCrashReport
  1045. emailer originally from Raven/Quake 4
  1046. ====================
  1047. */
  1048. void EmailCrashReport( LPSTR messageText ) {
  1049. LPMAPISENDMAIL MAPISendMail;
  1050. MapiMessage message;
  1051. static int lastEmailTime = 0;
  1052. if ( Sys_Milliseconds() < lastEmailTime + 10000 ) {
  1053. return;
  1054. }
  1055. lastEmailTime = Sys_Milliseconds();
  1056. HINSTANCE mapi = LoadLibrary( "MAPI32.DLL" );
  1057. if( mapi ) {
  1058. MAPISendMail = ( LPMAPISENDMAIL )GetProcAddress( mapi, "MAPISendMail" );
  1059. if( MAPISendMail ) {
  1060. MapiRecipDesc toProgrammers =
  1061. {
  1062. 0, // ulReserved
  1063. MAPI_TO, // ulRecipClass
  1064. "DOOM 3 Crash", // lpszName
  1065. "SMTP:programmers@idsoftware.com", // lpszAddress
  1066. 0, // ulEIDSize
  1067. 0 // lpEntry
  1068. };
  1069. memset( &message, 0, sizeof( message ) );
  1070. message.lpszSubject = "DOOM 3 Fatal Error";
  1071. message.lpszNoteText = messageText;
  1072. message.nRecipCount = 1;
  1073. message.lpRecips = &toProgrammers;
  1074. MAPISendMail(
  1075. 0, // LHANDLE lhSession
  1076. 0, // ULONG ulUIParam
  1077. &message, // lpMapiMessage lpMessage
  1078. MAPI_DIALOG, // FLAGS flFlags
  1079. 0 // ULONG ulReserved
  1080. );
  1081. }
  1082. FreeLibrary( mapi );
  1083. }
  1084. }
  1085. int Sys_FPU_PrintStateFlags( char *ptr, int ctrl, int stat, int tags, int inof, int inse, int opof, int opse );
  1086. /*
  1087. ====================
  1088. _except_handler
  1089. ====================
  1090. */
  1091. EXCEPTION_DISPOSITION __cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord, void * EstablisherFrame,
  1092. struct _CONTEXT *ContextRecord, void * DispatcherContext ) {
  1093. static char msg[ 8192 ];
  1094. char FPUFlags[2048];
  1095. Sys_FPU_PrintStateFlags( FPUFlags, ContextRecord->FloatSave.ControlWord,
  1096. ContextRecord->FloatSave.StatusWord,
  1097. ContextRecord->FloatSave.TagWord,
  1098. ContextRecord->FloatSave.ErrorOffset,
  1099. ContextRecord->FloatSave.ErrorSelector,
  1100. ContextRecord->FloatSave.DataOffset,
  1101. ContextRecord->FloatSave.DataSelector );
  1102. sprintf( msg,
  1103. "Please describe what you were doing when DOOM 3 crashed!\n"
  1104. "If this text did not pop into your email client please copy and email it to programmers@idsoftware.com\n"
  1105. "\n"
  1106. "-= FATAL EXCEPTION =-\n"
  1107. "\n"
  1108. "%s\n"
  1109. "\n"
  1110. "0x%x at address 0x%08x\n"
  1111. "\n"
  1112. "%s\n"
  1113. "\n"
  1114. "EAX = 0x%08x EBX = 0x%08x\n"
  1115. "ECX = 0x%08x EDX = 0x%08x\n"
  1116. "ESI = 0x%08x EDI = 0x%08x\n"
  1117. "EIP = 0x%08x ESP = 0x%08x\n"
  1118. "EBP = 0x%08x EFL = 0x%08x\n"
  1119. "\n"
  1120. "CS = 0x%04x\n"
  1121. "SS = 0x%04x\n"
  1122. "DS = 0x%04x\n"
  1123. "ES = 0x%04x\n"
  1124. "FS = 0x%04x\n"
  1125. "GS = 0x%04x\n"
  1126. "\n"
  1127. "%s\n",
  1128. com_version.GetString(),
  1129. ExceptionRecord->ExceptionCode,
  1130. ExceptionRecord->ExceptionAddress,
  1131. GetExceptionCodeInfo( ExceptionRecord->ExceptionCode ),
  1132. ContextRecord->Eax, ContextRecord->Ebx,
  1133. ContextRecord->Ecx, ContextRecord->Edx,
  1134. ContextRecord->Esi, ContextRecord->Edi,
  1135. ContextRecord->Eip, ContextRecord->Esp,
  1136. ContextRecord->Ebp, ContextRecord->EFlags,
  1137. ContextRecord->SegCs,
  1138. ContextRecord->SegSs,
  1139. ContextRecord->SegDs,
  1140. ContextRecord->SegEs,
  1141. ContextRecord->SegFs,
  1142. ContextRecord->SegGs,
  1143. FPUFlags
  1144. );
  1145. EmailCrashReport( msg );
  1146. common->FatalError( msg );
  1147. // Tell the OS to restart the faulting instruction
  1148. return ExceptionContinueExecution;
  1149. }
  1150. #define TEST_FPU_EXCEPTIONS /* FPU_EXCEPTION_INVALID_OPERATION | */ \
  1151. /* FPU_EXCEPTION_DENORMALIZED_OPERAND | */ \
  1152. /* FPU_EXCEPTION_DIVIDE_BY_ZERO | */ \
  1153. /* FPU_EXCEPTION_NUMERIC_OVERFLOW | */ \
  1154. /* FPU_EXCEPTION_NUMERIC_UNDERFLOW | */ \
  1155. /* FPU_EXCEPTION_INEXACT_RESULT | */ \
  1156. 0
  1157. #if defined(MACOS_X)
  1158. #define UMP_SKU "OSX"
  1159. #elif defined( __linux__ )
  1160. #define UMP_SKU "Linux"
  1161. #else
  1162. #define UMP_SKU "Win32"
  1163. #endif
  1164. void MiniDumpFunction( unsigned int nExceptionCode, EXCEPTION_POINTERS *pException )
  1165. {
  1166. }
  1167. /*
  1168. ==================
  1169. WinMain
  1170. ==================
  1171. */
  1172. int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
  1173. const HCURSOR hcurSave = ::SetCursor( LoadCursor( 0, IDC_WAIT ) );
  1174. Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 );
  1175. Sys_GetCurrentMemoryStatus( exeLaunchMemoryStats );
  1176. #if 0
  1177. DWORD handler = (DWORD)_except_handler;
  1178. __asm
  1179. { // Build EXCEPTION_REGISTRATION record:
  1180. push handler // Address of handler function
  1181. push FS:[0] // Address of previous handler
  1182. mov FS:[0],ESP // Install new EXECEPTION_REGISTRATION
  1183. }
  1184. #endif
  1185. win32.hInstance = hInstance;
  1186. idStr::Copynz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) );
  1187. #ifndef _DEBUG
  1188. _set_se_translator( MiniDumpFunction );
  1189. #endif
  1190. // done before Com/Sys_Init since we need this for error output
  1191. Sys_CreateConsole();
  1192. // no abort/retry/fail errors
  1193. SetErrorMode( SEM_FAILCRITICALERRORS );
  1194. for ( int i = 0; i < MAX_CRITICAL_SECTIONS; i++ ) {
  1195. InitializeCriticalSection( &win32.criticalSections[i] );
  1196. }
  1197. // get the initial time base
  1198. Sys_Milliseconds();
  1199. #ifdef DEBUG
  1200. // disable the painfully slow MS heap check every 1024 allocs
  1201. _CrtSetDbgFlag( 0 );
  1202. #endif
  1203. // Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
  1204. Sys_FPU_SetPrecision( FPU_PRECISION_DOUBLE_EXTENDED );
  1205. common->Init( 0, NULL, lpCmdLine );
  1206. #if TEST_FPU_EXCEPTIONS != 0
  1207. common->Printf( Sys_FPU_GetState() );
  1208. #endif
  1209. #ifndef ID_DEDICATED
  1210. if ( win32.win_notaskkeys.GetInteger() ) {
  1211. DisableTaskKeys( TRUE, FALSE, /*( win32.win_notaskkeys.GetInteger() == 2 )*/ FALSE );
  1212. }
  1213. #endif
  1214. Sys_StartAsyncThread();
  1215. // hide or show the early console as necessary
  1216. if ( win32.win_viewlog.GetInteger() || com_skipRenderer.GetBool() || idAsyncNetwork::serverDedicated.GetInteger() ) {
  1217. Sys_ShowConsole( 1, true );
  1218. } else {
  1219. Sys_ShowConsole( 0, false );
  1220. }
  1221. #ifdef SET_THREAD_AFFINITY
  1222. // give the main thread an affinity for the first cpu
  1223. SetThreadAffinityMask( GetCurrentThread(), 1 );
  1224. #endif
  1225. ::SetCursor( hcurSave );
  1226. // Launch the script debugger
  1227. if ( strstr( lpCmdLine, "+debugger" ) ) {
  1228. // DebuggerClientInit( lpCmdLine );
  1229. return 0;
  1230. }
  1231. ::SetFocus( win32.hWnd );
  1232. // main game loop
  1233. while( 1 ) {
  1234. Win_Frame();
  1235. #ifdef DEBUG
  1236. Sys_MemFrame();
  1237. #endif
  1238. // set exceptions, even if some crappy syscall changes them!
  1239. Sys_FPU_EnableExceptions( TEST_FPU_EXCEPTIONS );
  1240. #if ENABLE_FP_EXCEPTIONS
  1241. FPExceptionEnabler enabled(_EM_UNDERFLOW | _EM_OVERFLOW | _EM_ZERODIVIDE | _EM_INVALID);
  1242. #endif
  1243. #ifdef ID_ALLOW_TOOLS
  1244. if ( com_editors ) {
  1245. if ( com_editors & EDITOR_GUI ) {
  1246. // GUI editor
  1247. GUIEditorRun();
  1248. } else if ( com_editors & EDITOR_RADIANT ) {
  1249. // Level Editor
  1250. RadiantRun();
  1251. }
  1252. else if (com_editors & EDITOR_MATERIAL ) {
  1253. //BSM Nerve: Add support for the material editor
  1254. MaterialEditorRun();
  1255. }
  1256. else {
  1257. if ( com_editors & EDITOR_LIGHT ) {
  1258. // in-game Light Editor
  1259. LightEditorRun();
  1260. }
  1261. if ( com_editors & EDITOR_SOUND ) {
  1262. // in-game Sound Editor
  1263. SoundEditorRun();
  1264. }
  1265. if ( com_editors & EDITOR_DECL ) {
  1266. // in-game Declaration Browser
  1267. DeclBrowserRun();
  1268. }
  1269. if ( com_editors & EDITOR_AF ) {
  1270. // in-game Articulated Figure Editor
  1271. AFEditorRun();
  1272. }
  1273. if ( com_editors & EDITOR_PARTICLE ) {
  1274. // in-game Particle Editor
  1275. ParticleEditorRun();
  1276. }
  1277. if ( com_editors & EDITOR_SCRIPT ) {
  1278. // in-game Script Editor
  1279. ScriptEditorRun();
  1280. }
  1281. if ( com_editors & EDITOR_PDA ) {
  1282. // in-game PDA Editor
  1283. PDAEditorRun();
  1284. }
  1285. }
  1286. }
  1287. #endif
  1288. // run the game
  1289. common->Frame();
  1290. }
  1291. // never gets here
  1292. return 0;
  1293. }
  1294. /*
  1295. ====================
  1296. clrstk
  1297. I tried to get the run time to call this at every function entry, but
  1298. ====================
  1299. */
  1300. static int parmBytes;
  1301. __declspec( naked ) void clrstk( void ) {
  1302. // eax = bytes to add to stack
  1303. __asm {
  1304. mov [parmBytes],eax
  1305. neg eax ; compute new stack pointer in eax
  1306. add eax,esp
  1307. add eax,4
  1308. xchg eax,esp
  1309. mov eax,dword ptr [eax] ; copy the return address
  1310. push eax
  1311. ; clear to zero
  1312. push edi
  1313. push ecx
  1314. mov edi,esp
  1315. add edi,12
  1316. mov ecx,[parmBytes]
  1317. shr ecx,2
  1318. xor eax,eax
  1319. cld
  1320. rep stosd
  1321. pop ecx
  1322. pop edi
  1323. ret
  1324. }
  1325. }
  1326. /*
  1327. ==================
  1328. idSysLocal::OpenURL
  1329. ==================
  1330. */
  1331. void idSysLocal::OpenURL( const char *url, bool doexit ) {
  1332. static bool doexit_spamguard = false;
  1333. HWND wnd;
  1334. if (doexit_spamguard) {
  1335. common->DPrintf( "OpenURL: already in an exit sequence, ignoring %s\n", url );
  1336. return;
  1337. }
  1338. common->Printf("Open URL: %s\n", url);
  1339. if ( !ShellExecute( NULL, "open", url, NULL, NULL, SW_RESTORE ) ) {
  1340. common->Error( "Could not open url: '%s' ", url );
  1341. return;
  1342. }
  1343. wnd = GetForegroundWindow();
  1344. if ( wnd ) {
  1345. ShowWindow( wnd, SW_MAXIMIZE );
  1346. }
  1347. if ( doexit ) {
  1348. doexit_spamguard = true;
  1349. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
  1350. }
  1351. }
  1352. /*
  1353. ==================
  1354. idSysLocal::StartProcess
  1355. ==================
  1356. */
  1357. void idSysLocal::StartProcess( const char *exePath, bool doexit ) {
  1358. TCHAR szPathOrig[_MAX_PATH];
  1359. STARTUPINFO si;
  1360. PROCESS_INFORMATION pi;
  1361. ZeroMemory( &si, sizeof(si) );
  1362. si.cb = sizeof(si);
  1363. strncpy( szPathOrig, exePath, _MAX_PATH );
  1364. if( !CreateProcess( NULL, szPathOrig, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) ) {
  1365. common->Error( "Could not start process: '%s' ", szPathOrig );
  1366. return;
  1367. }
  1368. if ( doexit ) {
  1369. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
  1370. }
  1371. }
  1372. /*
  1373. ==================
  1374. Sys_SetFatalError
  1375. ==================
  1376. */
  1377. void Sys_SetFatalError( const char *error ) {
  1378. }
  1379. /*
  1380. ==================
  1381. Sys_DoPreferences
  1382. ==================
  1383. */
  1384. void Sys_DoPreferences( void ) {
  1385. }