win_main.cpp 42 KB

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