Debug.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program 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. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: Debug.cpp
  24. //-----------------------------------------------------------------------------
  25. //
  26. // Westwood Studios Pacific.
  27. //
  28. // Confidential Information
  29. // Copyright (C) 2001 - All Rights Reserved
  30. //
  31. //-----------------------------------------------------------------------------
  32. //
  33. // Project: RTS3
  34. //
  35. // File name: Debug.cpp
  36. //
  37. // Created: Steven Johnson, August 2001
  38. //
  39. // Desc: Debug logging and other debug utilities
  40. //
  41. // ----------------------------------------------------------------------------
  42. // SYSTEM INCLUDES
  43. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  44. // USER INCLUDES
  45. #define DEBUG_THREADSAFE
  46. #ifdef DEBUG_THREADSAFE
  47. #include "Common/CriticalSection.h"
  48. #endif
  49. #include "Common/Debug.h"
  50. #include "Common/registry.h"
  51. #include "Common/SystemInfo.h"
  52. #include "Common/UnicodeString.h"
  53. #include "GameClient/GameText.h"
  54. #include "GameClient/Keyboard.h"
  55. #include "GameClient/Mouse.h"
  56. #include "Common/StackDump.h"
  57. // Horrible reference, but we really, really need to know if we are windowed.
  58. extern bool DX8Wrapper_IsWindowed;
  59. extern HWND ApplicationHWnd;
  60. extern char *gAppPrefix; /// So WB can have a different log file name.
  61. #ifdef _INTERNAL
  62. // this should ALWAYS be present
  63. #pragma optimize("", off)
  64. #endif
  65. // ----------------------------------------------------------------------------
  66. // DEFINES
  67. // ----------------------------------------------------------------------------
  68. #ifdef DEBUG_LOGGING
  69. #if defined(_INTERNAL)
  70. #define DEBUG_FILE_NAME "DebugLogFileI.txt"
  71. #define DEBUG_FILE_NAME_PREV "DebugLogFilePrevI.txt"
  72. #elif defined(_DEBUG)
  73. #define DEBUG_FILE_NAME "DebugLogFileD.txt"
  74. #define DEBUG_FILE_NAME_PREV "DebugLogFilePrevD.txt"
  75. #else
  76. #define DEBUG_FILE_NAME "DebugLogFile.txt"
  77. #define DEBUG_FILE_NAME_PREV "DebugLogFilePrev.txt"
  78. #endif
  79. #endif
  80. // ----------------------------------------------------------------------------
  81. // PRIVATE TYPES
  82. // ----------------------------------------------------------------------------
  83. // ----------------------------------------------------------------------------
  84. // PRIVATE DATA
  85. // ----------------------------------------------------------------------------
  86. #ifdef DEBUG_LOGGING
  87. static FILE *theLogFile = NULL;
  88. #endif
  89. #define LARGE_BUFFER 8192
  90. static char theBuffer[ LARGE_BUFFER ]; // make it big to avoid weird overflow bugs in debug mode
  91. static int theDebugFlags = 0;
  92. static DWORD theMainThreadID = 0;
  93. // ----------------------------------------------------------------------------
  94. // PUBLIC DATA
  95. // ----------------------------------------------------------------------------
  96. char* TheCurrentIgnoreCrashPtr = NULL;
  97. #ifdef DEBUG_LOGGING
  98. UnsignedInt DebugLevelMask = 0;
  99. const char *TheDebugLevels[DEBUG_LEVEL_MAX] = {
  100. "NET"
  101. };
  102. #endif
  103. // ----------------------------------------------------------------------------
  104. // PRIVATE PROTOTYPES
  105. // ----------------------------------------------------------------------------
  106. static const char *getCurrentTimeString(void);
  107. static const char *getCurrentTickString(void);
  108. static const char *prepBuffer(const char* format, char *buffer);
  109. #ifdef DEBUG_LOGGING
  110. static void doLogOutput(const char *buffer);
  111. #endif
  112. static int doCrashBox(const char *buffer, Bool logResult);
  113. static void whackFunnyCharacters(char *buf);
  114. #ifdef DEBUG_STACKTRACE
  115. static void doStackDump();
  116. #endif
  117. // ----------------------------------------------------------------------------
  118. // PRIVATE FUNCTIONS
  119. // ----------------------------------------------------------------------------
  120. // ----------------------------------------------------------------------------
  121. inline Bool ignoringAsserts()
  122. {
  123. #if defined(_DEBUG) || defined(_INTERNAL)
  124. return !DX8Wrapper_IsWindowed || TheGlobalData->m_debugIgnoreAsserts;
  125. #else
  126. return !DX8Wrapper_IsWindowed;
  127. #endif
  128. }
  129. // ----------------------------------------------------------------------------
  130. inline HWND getThreadHWND()
  131. {
  132. return (theMainThreadID == GetCurrentThreadId())?ApplicationHWnd:NULL;
  133. }
  134. // ----------------------------------------------------------------------------
  135. int MessageBoxWrapper( LPCSTR lpText, LPCSTR lpCaption, UINT uType )
  136. {
  137. HWND threadHWND = getThreadHWND();
  138. if (!threadHWND)
  139. return (uType & MB_ABORTRETRYIGNORE)?IDIGNORE:IDYES;
  140. return ::MessageBox(threadHWND, lpText, lpCaption, uType);
  141. }
  142. // ----------------------------------------------------------------------------
  143. // getCurrentTimeString
  144. /**
  145. Return the current time in string form
  146. */
  147. // ----------------------------------------------------------------------------
  148. static const char *getCurrentTimeString(void)
  149. {
  150. time_t aclock;
  151. time(&aclock);
  152. struct tm *newtime = localtime(&aclock);
  153. return asctime(newtime);
  154. }
  155. // ----------------------------------------------------------------------------
  156. // getCurrentTickString
  157. /**
  158. Return the current TickCount in string form
  159. */
  160. // ----------------------------------------------------------------------------
  161. static const char *getCurrentTickString(void)
  162. {
  163. static char TheTickString[32];
  164. sprintf(TheTickString, "(T=%08lx)",::GetTickCount());
  165. return TheTickString;
  166. }
  167. // ----------------------------------------------------------------------------
  168. // prepBuffer
  169. // zap the buffer and optionally prepend the tick time.
  170. // ----------------------------------------------------------------------------
  171. /**
  172. Empty the buffer passed in, then optionally prepend the current TickCount
  173. value in string form, depending on the setting of theDebugFlags.
  174. */
  175. static const char *prepBuffer(const char* format, char *buffer)
  176. {
  177. buffer[0] = 0;
  178. #ifdef ALLOW_DEBUG_UTILS
  179. if (theDebugFlags & DEBUG_FLAG_PREPEND_TIME)
  180. {
  181. strcpy(buffer, getCurrentTickString());
  182. strcat(buffer, " ");
  183. }
  184. #endif
  185. return format;
  186. }
  187. // ----------------------------------------------------------------------------
  188. // doLogOutput
  189. /**
  190. send a string directly to the log file and/or console without further processing.
  191. */
  192. // ----------------------------------------------------------------------------
  193. #ifdef DEBUG_LOGGING
  194. static void doLogOutput(const char *buffer)
  195. {
  196. // log message to file
  197. if (theDebugFlags & DEBUG_FLAG_LOG_TO_FILE)
  198. {
  199. if (theLogFile)
  200. {
  201. fprintf(theLogFile, "%s", buffer); // note, no \n (should be there already)
  202. fflush(theLogFile);
  203. }
  204. }
  205. // log message to dev studio output window
  206. if (theDebugFlags & DEBUG_FLAG_LOG_TO_CONSOLE)
  207. {
  208. ::OutputDebugString(buffer);
  209. }
  210. }
  211. #endif
  212. // ----------------------------------------------------------------------------
  213. // doCrashBox
  214. /*
  215. present a messagebox with the given message. Depending on user selection,
  216. we exit the app, break into debugger, or continue execution.
  217. */
  218. // ----------------------------------------------------------------------------
  219. static int doCrashBox(const char *buffer, Bool logResult)
  220. {
  221. int result;
  222. if (!ignoringAsserts()) {
  223. //result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING|MB_DEFBUTTON3);
  224. result = MessageBoxWrapper(buffer, "Assertion Failure", MB_ABORTRETRYIGNORE|MB_TASKMODAL|MB_ICONWARNING);
  225. } else {
  226. result = IDIGNORE;
  227. }
  228. switch(result)
  229. {
  230. case IDABORT:
  231. #ifdef DEBUG_LOGGING
  232. if (logResult)
  233. DebugLog("[Abort]\n");
  234. #endif
  235. _exit(1);
  236. break;
  237. case IDRETRY:
  238. #ifdef DEBUG_LOGGING
  239. if (logResult)
  240. DebugLog("[Retry]\n");
  241. #endif
  242. ::DebugBreak();
  243. break;
  244. case IDIGNORE:
  245. #ifdef DEBUG_LOGGING
  246. // do nothing, just keep going
  247. if (logResult)
  248. DebugLog("[Ignore]\n");
  249. #endif
  250. break;
  251. }
  252. return result;
  253. }
  254. #ifdef DEBUG_STACKTRACE
  255. // ----------------------------------------------------------------------------
  256. /**
  257. Dumps a stack trace (from the current PC) to logfile and/or console.
  258. */
  259. static void doStackDump()
  260. {
  261. const int STACKTRACE_SIZE = 24;
  262. const int STACKTRACE_SKIP = 2;
  263. void* stacktrace[STACKTRACE_SIZE];
  264. doLogOutput("\nStack Dump:\n");
  265. ::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
  266. ::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, doLogOutput);
  267. }
  268. #endif
  269. // ----------------------------------------------------------------------------
  270. // whackFunnyCharacters
  271. /**
  272. Eliminates any undesirable nonprinting characters, aside from newline,
  273. replacing them with spaces.
  274. */
  275. // ----------------------------------------------------------------------------
  276. static void whackFunnyCharacters(char *buf)
  277. {
  278. for (char *p = buf + strlen(buf) - 1; p >= buf; --p)
  279. {
  280. // ok, these are naughty magic numbers, but I'm guessing you know ASCII....
  281. if (*p >= 0 && *p < 32 && *p != 10 && *p != 13)
  282. *p = 32;
  283. }
  284. }
  285. // ----------------------------------------------------------------------------
  286. // PUBLIC FUNCTIONS
  287. // ----------------------------------------------------------------------------
  288. // ----------------------------------------------------------------------------
  289. // DebugInit
  290. // ----------------------------------------------------------------------------
  291. #ifdef ALLOW_DEBUG_UTILS
  292. /**
  293. Initialize the debug utilities. This should be called once, as near to the
  294. start of the app as possible, before anything else (since other code will
  295. probably want to make use of it).
  296. */
  297. void DebugInit(int flags)
  298. {
  299. // if (theDebugFlags != 0)
  300. // ::MessageBox(NULL, "Debug already inited", "", MB_OK|MB_APPLMODAL);
  301. // just quietly allow multiple calls to this, so that static ctors can call it.
  302. if (theDebugFlags == 0 && strcmp(gAppPrefix, "wb_") != 0)
  303. {
  304. theDebugFlags = flags;
  305. theMainThreadID = GetCurrentThreadId();
  306. #ifdef DEBUG_LOGGING
  307. char dirbuf[ _MAX_PATH ];
  308. ::GetModuleFileName( NULL, dirbuf, sizeof( dirbuf ) );
  309. char *pEnd = dirbuf + strlen( dirbuf );
  310. while( pEnd != dirbuf )
  311. {
  312. if( *pEnd == '\\' )
  313. {
  314. *(pEnd + 1) = 0;
  315. break;
  316. }
  317. pEnd--;
  318. }
  319. char prevbuf[ _MAX_PATH ];
  320. char curbuf[ _MAX_PATH ];
  321. strcpy(prevbuf, dirbuf);
  322. strcat(prevbuf, gAppPrefix);
  323. strcat(prevbuf, DEBUG_FILE_NAME_PREV);
  324. strcpy(curbuf, dirbuf);
  325. strcat(curbuf, gAppPrefix);
  326. strcat(curbuf, DEBUG_FILE_NAME);
  327. remove(prevbuf);
  328. rename(curbuf, prevbuf);
  329. theLogFile = fopen(curbuf, "w");
  330. if (theLogFile != NULL)
  331. {
  332. DebugLog("Log %s opened: %s\n", curbuf, getCurrentTimeString());
  333. }
  334. #endif
  335. }
  336. }
  337. #endif
  338. // ----------------------------------------------------------------------------
  339. // DebugLog
  340. // ----------------------------------------------------------------------------
  341. #ifdef DEBUG_LOGGING
  342. /**
  343. Print a character string to the logfile and/or console.
  344. */
  345. void DebugLog(const char *format, ...)
  346. {
  347. #ifdef DEBUG_THREADSAFE
  348. ScopedCriticalSection scopedCriticalSection(TheDebugLogCriticalSection);
  349. #endif
  350. if (theDebugFlags == 0)
  351. MessageBoxWrapper("DebugLog - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
  352. format = prepBuffer(format, theBuffer);
  353. va_list arg;
  354. va_start(arg, format);
  355. vsprintf(theBuffer + strlen(theBuffer), format, arg);
  356. va_end(arg);
  357. if (strlen(theBuffer) >= sizeof(theBuffer))
  358. MessageBoxWrapper("String too long for debug buffer", "", MB_OK|MB_TASKMODAL);
  359. whackFunnyCharacters(theBuffer);
  360. doLogOutput(theBuffer);
  361. }
  362. #endif
  363. // ----------------------------------------------------------------------------
  364. // DebugCrash
  365. // ----------------------------------------------------------------------------
  366. #ifdef DEBUG_CRASHING
  367. /**
  368. Print a character string to the logfile and/or console, then halt execution
  369. while presenting the user with an exit/debug/ignore dialog containing the same
  370. text message.
  371. */
  372. void DebugCrash(const char *format, ...)
  373. {
  374. // Note: You might want to make this thread safe, but we cannot. The reason is that
  375. // there is an implicit requirement on other threads that the message loop be running.
  376. // make it not static so that it'll be thread-safe.
  377. // make it big to avoid weird overflow bugs in debug mode
  378. char theCrashBuffer[ LARGE_BUFFER ];
  379. if (theDebugFlags == 0)
  380. {
  381. if (!DX8Wrapper_IsWindowed) {
  382. if (ApplicationHWnd) {
  383. ShowWindow(ApplicationHWnd, SW_HIDE);
  384. }
  385. }
  386. MessageBoxWrapper("DebugCrash - Debug not inited properly", "", MB_OK|MB_TASKMODAL);
  387. }
  388. format = prepBuffer(format, theCrashBuffer);
  389. strcat(theCrashBuffer, "ASSERTION FAILURE: ");
  390. va_list arg;
  391. va_start(arg, format);
  392. vsprintf(theCrashBuffer + strlen(theCrashBuffer), format, arg);
  393. va_end(arg);
  394. if (strlen(theCrashBuffer) >= sizeof(theCrashBuffer))
  395. {
  396. if (!DX8Wrapper_IsWindowed) {
  397. if (ApplicationHWnd) {
  398. ShowWindow(ApplicationHWnd, SW_HIDE);
  399. }
  400. }
  401. MessageBoxWrapper("String too long for debug buffers", "", MB_OK|MB_TASKMODAL);
  402. }
  403. #ifdef DEBUG_LOGGING
  404. if (ignoringAsserts())
  405. {
  406. doLogOutput("**** CRASH IN FULL SCREEN - Auto-ignored, CHECK THIS LOG!\n");
  407. }
  408. whackFunnyCharacters(theCrashBuffer);
  409. doLogOutput(theCrashBuffer);
  410. #endif
  411. #ifdef DEBUG_STACKTRACE
  412. if (!TheGlobalData->m_debugIgnoreStackTrace)
  413. {
  414. doStackDump();
  415. }
  416. #endif
  417. strcat(theCrashBuffer, "\n\nAbort->exception; Retry->debugger; Ignore->continue\n");
  418. int result = doCrashBox(theCrashBuffer, true);
  419. if (result == IDIGNORE && TheCurrentIgnoreCrashPtr != NULL)
  420. {
  421. int yn;
  422. if (!ignoringAsserts())
  423. {
  424. yn = MessageBoxWrapper("Ignore this crash from now on?", "", MB_YESNO|MB_TASKMODAL);
  425. }
  426. else
  427. {
  428. yn = IDYES;
  429. }
  430. if (yn == IDYES)
  431. *TheCurrentIgnoreCrashPtr = 1;
  432. if( TheKeyboard )
  433. TheKeyboard->resetKeys();
  434. if( TheMouse )
  435. TheMouse->reset();
  436. }
  437. }
  438. #endif
  439. // ----------------------------------------------------------------------------
  440. // DebugShutdown
  441. // ----------------------------------------------------------------------------
  442. #ifdef ALLOW_DEBUG_UTILS
  443. /**
  444. Shut down the debug utilities. This should be called once, as near to the
  445. end of the app as possible, after everything else (since other code will
  446. probably want to make use of it).
  447. */
  448. void DebugShutdown()
  449. {
  450. #ifdef DEBUG_LOGGING
  451. if (theLogFile)
  452. {
  453. DebugLog("Log closed: %s\n", getCurrentTimeString());
  454. fclose(theLogFile);
  455. }
  456. theLogFile = NULL;
  457. #endif
  458. theDebugFlags = 0;
  459. }
  460. // ----------------------------------------------------------------------------
  461. // DebugGetFlags
  462. // ----------------------------------------------------------------------------
  463. /**
  464. Get the current values for the flags passed to DebugInit. Most code will never
  465. need to use this; the most common usage would be to temporarily enable or disable
  466. the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
  467. */
  468. int DebugGetFlags()
  469. {
  470. return theDebugFlags;
  471. }
  472. // ----------------------------------------------------------------------------
  473. // DebugSetFlags
  474. // ----------------------------------------------------------------------------
  475. /**
  476. Set the current values for the flags passed to DebugInit. Most code will never
  477. need to use this; the most common usage would be to temporarily enable or disable
  478. the DEBUG_FLAG_PREPEND_TIME bit for complex logfile messages.
  479. */
  480. void DebugSetFlags(int flags)
  481. {
  482. theDebugFlags = flags;
  483. }
  484. #endif // ALLOW_DEBUG_UTILS
  485. #ifdef ALLOW_DEBUG_UTILS
  486. // ----------------------------------------------------------------------------
  487. SimpleProfiler::SimpleProfiler()
  488. {
  489. QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq);
  490. m_startThisSession = 0;
  491. m_totalThisSession = 0;
  492. m_totalAllSessions = 0;
  493. m_numSessions = 0;
  494. }
  495. // ----------------------------------------------------------------------------
  496. void SimpleProfiler::start()
  497. {
  498. DEBUG_ASSERTCRASH(m_startThisSession == 0, ("already started"));
  499. QueryPerformanceCounter((LARGE_INTEGER*)&m_startThisSession);
  500. }
  501. // ----------------------------------------------------------------------------
  502. void SimpleProfiler::stop()
  503. {
  504. if (m_startThisSession != 0)
  505. {
  506. __int64 stop;
  507. QueryPerformanceCounter((LARGE_INTEGER*)&stop);
  508. m_totalThisSession = stop - m_startThisSession;
  509. m_totalAllSessions += stop - m_startThisSession;
  510. m_startThisSession = 0;
  511. ++m_numSessions;
  512. }
  513. }
  514. // ----------------------------------------------------------------------------
  515. void SimpleProfiler::stopAndLog(const char *msg, int howOftenToLog, int howOftenToResetAvg)
  516. {
  517. stop();
  518. // howOftenToResetAvg==0 means "never reset"
  519. if (howOftenToResetAvg > 0 && m_numSessions >= howOftenToResetAvg)
  520. {
  521. m_numSessions = 0;
  522. m_totalAllSessions = 0;
  523. DEBUG_LOG(("%s: reset averages\n",msg));
  524. }
  525. DEBUG_ASSERTLOG(m_numSessions % howOftenToLog != 0, ("%s: %f msec, total %f msec, avg %f msec\n",msg,getTime(),getTotalTime(),getAverageTime()));
  526. }
  527. // ----------------------------------------------------------------------------
  528. double SimpleProfiler::getTime()
  529. {
  530. stop();
  531. return (double)m_totalThisSession / (double)m_freq * 1000.0;
  532. }
  533. // ----------------------------------------------------------------------------
  534. int SimpleProfiler::getNumSessions()
  535. {
  536. stop();
  537. return m_numSessions;
  538. }
  539. // ----------------------------------------------------------------------------
  540. double SimpleProfiler::getTotalTime()
  541. {
  542. stop();
  543. if (!m_numSessions)
  544. return 0.0;
  545. return (double)m_totalAllSessions * 1000.0 / ((double)m_freq);
  546. }
  547. // ----------------------------------------------------------------------------
  548. double SimpleProfiler::getAverageTime()
  549. {
  550. stop();
  551. if (!m_numSessions)
  552. return 0.0;
  553. return (double)m_totalAllSessions * 1000.0 / ((double)m_freq * (double)m_numSessions);
  554. }
  555. #endif // ALLOW_DEBUG_UTILS
  556. // ----------------------------------------------------------------------------
  557. // ReleaseCrash
  558. // ----------------------------------------------------------------------------
  559. /**
  560. Halt the application, EVEN IN FINAL RELEASE BUILDS. This should be called
  561. only when a crash is guaranteed by continuing, and no meaningful continuation
  562. of processing is possible, even by throwing an exception.
  563. */
  564. #define RELEASECRASH_FILE_NAME "ReleaseCrashInfo.txt"
  565. #define RELEASECRASH_FILE_NAME_PREV "ReleaseCrashInfoPrev.txt"
  566. static FILE *theReleaseCrashLogFile = NULL;
  567. static void releaseCrashLogOutput(const char *buffer)
  568. {
  569. if (theReleaseCrashLogFile)
  570. {
  571. fprintf(theReleaseCrashLogFile, "%s", buffer); // note, no \n (should be there already)
  572. fflush(theReleaseCrashLogFile);
  573. }
  574. }
  575. void ReleaseCrash(const char *reason)
  576. {
  577. /// do additional reporting on the crash, if possible
  578. char prevbuf[ _MAX_PATH ];
  579. char curbuf[ _MAX_PATH ];
  580. strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
  581. strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
  582. strcpy(curbuf, TheGlobalData->getPath_UserData().str());
  583. strcat(curbuf, RELEASECRASH_FILE_NAME);
  584. remove(prevbuf);
  585. rename(curbuf, prevbuf);
  586. theReleaseCrashLogFile = fopen(curbuf, "w");
  587. if (theReleaseCrashLogFile)
  588. {
  589. fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), reason);
  590. fprintf(theReleaseCrashLogFile, "\nLast error:\n%s\n\nCurrent stack:\n", g_LastErrorDump.str());
  591. const int STACKTRACE_SIZE = 12;
  592. const int STACKTRACE_SKIP = 6;
  593. void* stacktrace[STACKTRACE_SIZE];
  594. ::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
  595. ::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
  596. fflush(theReleaseCrashLogFile);
  597. fclose(theReleaseCrashLogFile);
  598. theReleaseCrashLogFile = NULL;
  599. }
  600. if (!DX8Wrapper_IsWindowed) {
  601. if (ApplicationHWnd) {
  602. ShowWindow(ApplicationHWnd, SW_HIDE);
  603. }
  604. }
  605. #if defined(_DEBUG) || defined(_INTERNAL)
  606. /* static */ char buff[8192]; // not so static so we can be threadsafe
  607. _snprintf(buff, 8192, "Sorry, a serious error occurred. (%s)", reason);
  608. buff[8191] = 0;
  609. ::MessageBox(NULL, buff, "Technical Difficulties...", MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
  610. #else
  611. // crash error messaged changed 3/6/03 BGC
  612. // ::MessageBox(NULL, "Sorry, a serious error occurred.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
  613. if (!GetRegistryLanguage().compareNoCase("german2") || !GetRegistryLanguage().compareNoCase("german") )
  614. {
  615. ::MessageBox(NULL, "Es ist ein gravierender Fehler aufgetreten. Solche Fehler können durch viele verschiedene Dinge wie Viren, überhitzte Hardware und Hardware, die den Mindestanforderungen des Spiels nicht entspricht, ausgelöst werden. Tipps zur Vorgehensweise findest du in den Foren unter www.generals.ea.com, Informationen zum Technischen Kundendienst im Handbuch zum Spiel.", "Fehler...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
  616. }
  617. else
  618. {
  619. ::MessageBox(NULL, "You have encountered a serious error. Serious errors can be caused by many things including viruses, overheated hardware and hardware that does not meet the minimum specifications for the game. Please visit the forums at www.generals.ea.com for suggested courses of action or consult your manual for Technical Support contact information.", "Technical Difficulties...", MB_OK|MB_TASKMODAL|MB_ICONERROR);
  620. }
  621. #endif
  622. _exit(1);
  623. }
  624. void ReleaseCrashLocalized(const AsciiString& p, const AsciiString& m)
  625. {
  626. if (!TheGameText) {
  627. ReleaseCrash(m.str());
  628. // This won't ever return
  629. return;
  630. }
  631. UnicodeString prompt = TheGameText->fetch(p);
  632. UnicodeString mesg = TheGameText->fetch(m);
  633. /// do additional reporting on the crash, if possible
  634. if (!DX8Wrapper_IsWindowed) {
  635. if (ApplicationHWnd) {
  636. ShowWindow(ApplicationHWnd, SW_HIDE);
  637. }
  638. }
  639. if (TheSystemIsUnicode)
  640. {
  641. ::MessageBoxW(NULL, mesg.str(), prompt.str(), MB_OK|MB_SYSTEMMODAL|MB_ICONERROR);
  642. }
  643. else
  644. {
  645. // However, if we're using the default version of the message box, we need to
  646. // translate the string into an AsciiString
  647. AsciiString promptA, mesgA;
  648. promptA.translate(prompt);
  649. mesgA.translate(mesg);
  650. //Make sure main window is not TOP_MOST
  651. ::SetWindowPos(ApplicationHWnd, HWND_NOTOPMOST, 0, 0, 0, 0,SWP_NOSIZE |SWP_NOMOVE);
  652. ::MessageBoxA(NULL, mesgA.str(), promptA.str(), MB_OK|MB_TASKMODAL|MB_ICONERROR);
  653. }
  654. char prevbuf[ _MAX_PATH ];
  655. char curbuf[ _MAX_PATH ];
  656. strcpy(prevbuf, TheGlobalData->getPath_UserData().str());
  657. strcat(prevbuf, RELEASECRASH_FILE_NAME_PREV);
  658. strcpy(curbuf, TheGlobalData->getPath_UserData().str());
  659. strcat(curbuf, RELEASECRASH_FILE_NAME);
  660. remove(prevbuf);
  661. rename(curbuf, prevbuf);
  662. theReleaseCrashLogFile = fopen(curbuf, "w");
  663. if (theReleaseCrashLogFile)
  664. {
  665. fprintf(theReleaseCrashLogFile, "Release Crash at %s; Reason %s\n", getCurrentTimeString(), mesg.str());
  666. const int STACKTRACE_SIZE = 12;
  667. const int STACKTRACE_SKIP = 6;
  668. void* stacktrace[STACKTRACE_SIZE];
  669. ::FillStackAddresses(stacktrace, STACKTRACE_SIZE, STACKTRACE_SKIP);
  670. ::StackDumpFromAddresses(stacktrace, STACKTRACE_SIZE, releaseCrashLogOutput);
  671. fflush(theReleaseCrashLogFile);
  672. fclose(theReleaseCrashLogFile);
  673. theReleaseCrashLogFile = NULL;
  674. }
  675. _exit(1);
  676. }