1 /* $Id: crashlog_win.cpp 25677 2013-08-05 20:36:58Z michi_cc $ */
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file crashlog_win.cpp Implementation of a crashlogger for Windows */
12 #include "../../stdafx.h"
13 #include "../../crashlog.h"
15 #include "../../core/alloc_func.hpp"
16 #include "../../core/math_func.hpp"
17 #include "../../string_func.h"
18 #include "../../fileio_func.h"
19 #include "../../strings_func.h"
20 #include "../../gamelog.h"
21 #include "../../saveload/saveload.h"
22 #include "../../video/video_driver.hpp"
23 #include "../../openttd.h"
28 #include "../../safeguards.h"
30 static const uint MAX_SYMBOL_LEN
= 512;
31 static const uint MAX_FRAMES
= 64;
33 /* printf format specification for 32/64-bit addresses. */
35 #define PRINTF_PTR "0x%016IX"
37 #define PRINTF_PTR "0x%08X"
41 * Windows implementation for the crash logger.
43 class CrashLogWindows
: public CrashLog
{
44 /** Information about the encountered exception */
45 EXCEPTION_POINTERS
*ep
;
47 /* virtual */ char *LogOSVersion(char *buffer
, const char *last
) const;
48 /* virtual */ char *LogError(char *buffer
, const char *last
, const char *message
) const;
49 /* virtual */ char *LogStacktrace(char *buffer
, const char *last
) const;
50 /* virtual */ char *LogRegisters(char *buffer
, const char *last
) const;
51 /* virtual */ char *LogModules(char *buffer
, const char *last
) const;
54 /* virtual */ int WriteCrashDump(char *filename
, const char *filename_last
) const;
55 char *AppendDecodedStacktrace(char *buffer
, const char *last
) const;
57 char *AppendDecodedStacktrace(char *buffer
, const char *last
) const { return buffer
; }
60 /** Buffer for the generated crash log */
61 char crashlog
[65536 * 4];
62 /** Buffer for the filename of the crash log */
63 char crashlog_filename
[MAX_PATH
];
64 /** Buffer for the filename of the crash dump */
65 char crashdump_filename
[MAX_PATH
];
66 /** Buffer for the filename of the crash screenshot */
67 char screenshot_filename
[MAX_PATH
];
70 * A crash log is always generated when it's generated.
71 * @param ep the data related to the exception.
73 CrashLogWindows(EXCEPTION_POINTERS
*ep
= NULL
) :
76 this->crashlog
[0] = '\0';
77 this->crashlog_filename
[0] = '\0';
78 this->crashdump_filename
[0] = '\0';
79 this->screenshot_filename
[0] = '\0';
83 * Points to the current crash log.
85 static CrashLogWindows
*current
;
88 /* static */ CrashLogWindows
*CrashLogWindows::current
= NULL
;
90 /* virtual */ char *CrashLogWindows::LogOSVersion(char *buffer
, const char *last
) const
93 os
.dwOSVersionInfoSize
= sizeof(os
);
96 return buffer
+ seprintf(buffer
, last
,
99 " Release: %d.%d.%d (%s)\n",
100 (int)os
.dwMajorVersion
,
101 (int)os
.dwMinorVersion
,
102 (int)os
.dwBuildNumber
,
108 /* virtual */ char *CrashLogWindows::LogError(char *buffer
, const char *last
, const char *message
) const
110 return buffer
+ seprintf(buffer
, last
,
114 " Location: %.16IX\n"
119 (int)ep
->ExceptionRecord
->ExceptionCode
,
120 (size_t)ep
->ExceptionRecord
->ExceptionAddress
,
121 message
== NULL
? "<none>" : message
125 struct DebugFileInfo
{
128 SYSTEMTIME file_time
;
131 static uint32
*_crc_table
;
133 static void MakeCRCTable(uint32
*table
)
135 uint32 crc
, poly
= 0xEDB88320L
;
141 for (i
= 0; i
!= 256; i
++) {
143 for (j
= 8; j
!= 0; j
--) {
144 crc
= (crc
& 1 ? (crc
>> 1) ^ poly
: crc
>> 1);
150 static uint32
CalcCRC(byte
*data
, uint size
, uint32 crc
)
152 for (; size
> 0; size
--) {
153 crc
= ((crc
>> 8) & 0x00FFFFFF) ^ _crc_table
[(crc
^ *data
++) & 0xFF];
158 static void GetFileInfo(DebugFileInfo
*dfi
, const TCHAR
*filename
)
161 memset(dfi
, 0, sizeof(*dfi
));
163 file
= CreateFile(filename
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
164 if (file
!= INVALID_HANDLE_VALUE
) {
169 uint32 crc
= (uint32
)-1;
172 if (ReadFile(file
, buffer
, sizeof(buffer
), &numread
, NULL
) == 0 || numread
== 0) {
176 crc
= CalcCRC(buffer
, numread
, crc
);
178 dfi
->size
= filesize
;
179 dfi
->crc32
= crc
^ (uint32
)-1;
181 if (GetFileTime(file
, NULL
, NULL
, &write_time
)) {
182 FileTimeToSystemTime(&write_time
, &dfi
->file_time
);
189 static char *PrintModuleInfo(char *output
, const char *last
, HMODULE mod
)
191 TCHAR buffer
[MAX_PATH
];
194 GetModuleFileName(mod
, buffer
, MAX_PATH
);
195 GetFileInfo(&dfi
, buffer
);
196 output
+= seprintf(output
, last
, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n",
202 dfi
.file_time
.wMonth
,
205 dfi
.file_time
.wMinute
,
206 dfi
.file_time
.wSecond
211 /* virtual */ char *CrashLogWindows::LogModules(char *output
, const char *last
) const
213 MakeCRCTable(AllocaM(uint32
, 256));
214 BOOL (WINAPI
*EnumProcessModules
)(HANDLE
, HMODULE
*, DWORD
, LPDWORD
);
216 output
+= seprintf(output
, last
, "Module information:\n");
218 if (LoadLibraryList((Function
*)&EnumProcessModules
, "psapi.dll\0EnumProcessModules\0\0")) {
219 HMODULE modules
[100];
223 HANDLE proc
= OpenProcess(PROCESS_ALL_ACCESS
, FALSE
, GetCurrentProcessId());
225 res
= EnumProcessModules(proc
, modules
, sizeof(modules
), &needed
);
228 size_t count
= min(needed
/ sizeof(HMODULE
), lengthof(modules
));
230 for (size_t i
= 0; i
!= count
; i
++) output
= PrintModuleInfo(output
, last
, modules
[i
]);
231 return output
+ seprintf(output
, last
, "\n");
235 output
= PrintModuleInfo(output
, last
, NULL
);
236 return output
+ seprintf(output
, last
, "\n");
239 /* virtual */ char *CrashLogWindows::LogRegisters(char *buffer
, const char *last
) const
241 buffer
+= seprintf(buffer
, last
, "Registers:\n");
243 buffer
+= seprintf(buffer
, last
,
244 " RAX: %.16I64X RBX: %.16I64X RCX: %.16I64X RDX: %.16I64X\n"
245 " RSI: %.16I64X RDI: %.16I64X RBP: %.16I64X RSP: %.16I64X\n"
246 " R8: %.16I64X R9: %.16I64X R10: %.16I64X R11: %.16I64X\n"
247 " R12: %.16I64X R13: %.16I64X R14: %.16I64X R15: %.16I64X\n"
248 " RIP: %.16I64X EFLAGS: %.8lX\n",
249 ep
->ContextRecord
->Rax
,
250 ep
->ContextRecord
->Rbx
,
251 ep
->ContextRecord
->Rcx
,
252 ep
->ContextRecord
->Rdx
,
253 ep
->ContextRecord
->Rsi
,
254 ep
->ContextRecord
->Rdi
,
255 ep
->ContextRecord
->Rbp
,
256 ep
->ContextRecord
->Rsp
,
257 ep
->ContextRecord
->R8
,
258 ep
->ContextRecord
->R9
,
259 ep
->ContextRecord
->R10
,
260 ep
->ContextRecord
->R11
,
261 ep
->ContextRecord
->R12
,
262 ep
->ContextRecord
->R13
,
263 ep
->ContextRecord
->R14
,
264 ep
->ContextRecord
->R15
,
265 ep
->ContextRecord
->Rip
,
266 ep
->ContextRecord
->EFlags
269 buffer
+= seprintf(buffer
, last
,
270 " EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\n"
271 " ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\n"
272 " EIP: %.8X EFLAGS: %.8X\n",
273 (int)ep
->ContextRecord
->Eax
,
274 (int)ep
->ContextRecord
->Ebx
,
275 (int)ep
->ContextRecord
->Ecx
,
276 (int)ep
->ContextRecord
->Edx
,
277 (int)ep
->ContextRecord
->Esi
,
278 (int)ep
->ContextRecord
->Edi
,
279 (int)ep
->ContextRecord
->Ebp
,
280 (int)ep
->ContextRecord
->Esp
,
281 (int)ep
->ContextRecord
->Eip
,
282 (int)ep
->ContextRecord
->EFlags
286 buffer
+= seprintf(buffer
, last
, "\n Bytes at instruction pointer:\n");
288 byte
*b
= (byte
*)ep
->ContextRecord
->Rip
;
290 byte
*b
= (byte
*)ep
->ContextRecord
->Eip
;
292 for (int i
= 0; i
!= 24; i
++) {
293 if (IsBadReadPtr(b
, 1)) {
294 buffer
+= seprintf(buffer
, last
, " ??"); // OCR: WAS: , 0);
296 buffer
+= seprintf(buffer
, last
, " %.2X", *b
);
300 return buffer
+ seprintf(buffer
, last
, "\n\n");
303 /* virtual */ char *CrashLogWindows::LogStacktrace(char *buffer
, const char *last
) const
305 buffer
+= seprintf(buffer
, last
, "Stack trace:\n");
307 uint32
*b
= (uint32
*)ep
->ContextRecord
->Rsp
;
309 uint32
*b
= (uint32
*)ep
->ContextRecord
->Esp
;
311 for (int j
= 0; j
!= 24; j
++) {
312 for (int i
= 0; i
!= 8; i
++) {
313 if (IsBadReadPtr(b
, sizeof(uint32
))) {
314 buffer
+= seprintf(buffer
, last
, " ????????"); // OCR: WAS - , 0);
316 buffer
+= seprintf(buffer
, last
, " %.8X", *b
);
320 buffer
+= seprintf(buffer
, last
, "\n");
322 return buffer
+ seprintf(buffer
, last
, "\n");
325 #if defined(_MSC_VER)
326 #pragma warning(disable:4091)
328 #pragma warning(default:4091)
330 char *CrashLogWindows::AppendDecodedStacktrace(char *buffer
, const char *last
) const
333 static const char dbg_import
[] =
339 M("SymFunctionTableAccess64")
340 M("SymGetModuleBase64")
341 M("SymGetModuleInfo64")
342 M("SymGetSymFromAddr64")
343 M("SymGetLineFromAddr64")
349 BOOL (WINAPI
* pSymInitialize
)(HANDLE
, PCSTR
, BOOL
);
350 BOOL (WINAPI
* pSymSetOptions
)(DWORD
);
351 BOOL (WINAPI
* pSymCleanup
)(HANDLE
);
352 BOOL (WINAPI
* pStackWalk64
)(DWORD
, HANDLE
, HANDLE
, LPSTACKFRAME64
, PVOID
, PREAD_PROCESS_MEMORY_ROUTINE64
, PFUNCTION_TABLE_ACCESS_ROUTINE64
, PGET_MODULE_BASE_ROUTINE64
, PTRANSLATE_ADDRESS_ROUTINE64
);
353 PVOID (WINAPI
* pSymFunctionTableAccess64
)(HANDLE
, DWORD64
);
354 DWORD64 (WINAPI
* pSymGetModuleBase64
)(HANDLE
, DWORD64
);
355 BOOL (WINAPI
* pSymGetModuleInfo64
)(HANDLE
, DWORD64
, PIMAGEHLP_MODULE64
);
356 BOOL (WINAPI
* pSymGetSymFromAddr64
)(HANDLE
, DWORD64
, PDWORD64
, PIMAGEHLP_SYMBOL64
);
357 BOOL (WINAPI
* pSymGetLineFromAddr64
)(HANDLE
, DWORD64
, PDWORD
, PIMAGEHLP_LINE64
);
360 buffer
+= seprintf(buffer
, last
, "\nDecoded stack trace:\n");
362 /* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */
363 if (LoadLibraryList((Function
*)&proc
, dbg_import
)) {
364 /* Initialize symbol handler. */
365 HANDLE hCur
= GetCurrentProcess();
366 proc
.pSymInitialize(hCur
, NULL
, TRUE
);
367 /* Load symbols only when needed, fail silently on errors, demangle symbol names. */
368 proc
.pSymSetOptions(SYMOPT_DEFERRED_LOADS
| SYMOPT_FAIL_CRITICAL_ERRORS
| SYMOPT_UNDNAME
);
370 /* Initialize starting stack frame from context record. */
372 memset(&frame
, 0, sizeof(frame
));
374 frame
.AddrPC
.Offset
= ep
->ContextRecord
->Rip
;
375 frame
.AddrFrame
.Offset
= ep
->ContextRecord
->Rbp
;
376 frame
.AddrStack
.Offset
= ep
->ContextRecord
->Rsp
;
378 frame
.AddrPC
.Offset
= ep
->ContextRecord
->Eip
;
379 frame
.AddrFrame
.Offset
= ep
->ContextRecord
->Ebp
;
380 frame
.AddrStack
.Offset
= ep
->ContextRecord
->Esp
;
382 frame
.AddrPC
.Mode
= AddrModeFlat
;
383 frame
.AddrFrame
.Mode
= AddrModeFlat
;
384 frame
.AddrStack
.Mode
= AddrModeFlat
;
386 /* Copy context record as StackWalk64 may modify it. */
388 memcpy(&ctx
, ep
->ContextRecord
, sizeof(ctx
));
390 /* Allocate space for symbol info. */
391 IMAGEHLP_SYMBOL64
*sym_info
= (IMAGEHLP_SYMBOL64
*)alloca(sizeof(IMAGEHLP_SYMBOL64
) + MAX_SYMBOL_LEN
- 1);
392 sym_info
->SizeOfStruct
= sizeof(IMAGEHLP_SYMBOL64
);
393 sym_info
->MaxNameLength
= MAX_SYMBOL_LEN
;
395 /* Walk stack at most MAX_FRAMES deep in case the stack is corrupt. */
396 for (uint num
= 0; num
< MAX_FRAMES
; num
++) {
397 if (!proc
.pStackWalk64(
399 IMAGE_FILE_MACHINE_AMD64
,
401 IMAGE_FILE_MACHINE_I386
,
403 hCur
, GetCurrentThread(), &frame
, &ctx
, NULL
, proc
.pSymFunctionTableAccess64
, proc
.pSymGetModuleBase64
, NULL
)) break;
405 if (frame
.AddrPC
.Offset
== frame
.AddrReturn
.Offset
) {
406 buffer
+= seprintf(buffer
, last
, " <infinite loop>\n");
410 /* Get module name. */
411 const char *mod_name
= "???";
413 IMAGEHLP_MODULE64 module
;
414 module
.SizeOfStruct
= sizeof(module
);
415 if (proc
.pSymGetModuleInfo64(hCur
, frame
.AddrPC
.Offset
, &module
)) {
416 mod_name
= module
.ModuleName
;
419 /* Print module and instruction pointer. */
420 buffer
+= seprintf(buffer
, last
, "[%02d] %-20s " PRINTF_PTR
, num
, mod_name
, frame
.AddrPC
.Offset
);
422 /* Get symbol name and line info if possible. */
424 if (proc
.pSymGetSymFromAddr64(hCur
, frame
.AddrPC
.Offset
, &offset
, sym_info
)) {
425 buffer
+= seprintf(buffer
, last
, " %s + %I64u", sym_info
->Name
, offset
);
428 IMAGEHLP_LINE64 line
;
429 line
.SizeOfStruct
= sizeof(IMAGEHLP_LINE64
);
430 if (proc
.pSymGetLineFromAddr64(hCur
, frame
.AddrPC
.Offset
, &line_offs
, &line
)) {
431 buffer
+= seprintf(buffer
, last
, " (%s:%d)", line
.FileName
, line
.LineNumber
);
434 buffer
+= seprintf(buffer
, last
, "\n");
437 proc
.pSymCleanup(hCur
);
440 return buffer
+ seprintf(buffer
, last
, "\n*** End of additional info ***\n");
443 /* virtual */ int CrashLogWindows::WriteCrashDump(char *filename
, const char *filename_last
) const
446 HMODULE dbghelp
= LoadLibrary(_T("dbghelp.dll"));
447 if (dbghelp
!= NULL
) {
448 typedef BOOL (WINAPI
*MiniDumpWriteDump_t
)(HANDLE
, DWORD
, HANDLE
,
450 CONST PMINIDUMP_EXCEPTION_INFORMATION
,
451 CONST PMINIDUMP_USER_STREAM_INFORMATION
,
452 CONST PMINIDUMP_CALLBACK_INFORMATION
);
453 MiniDumpWriteDump_t funcMiniDumpWriteDump
= (MiniDumpWriteDump_t
)GetProcAddress(dbghelp
, "MiniDumpWriteDump");
454 if (funcMiniDumpWriteDump
!= NULL
) {
455 seprintf(filename
, filename_last
, "%scrash.dmp", _personal_dir
);
456 HANDLE file
= CreateFile(OTTD2FS(filename
), GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, 0);
457 HANDLE proc
= GetCurrentProcess();
458 DWORD procid
= GetCurrentProcessId();
459 MINIDUMP_EXCEPTION_INFORMATION mdei
;
460 MINIDUMP_USER_STREAM userstream
;
461 MINIDUMP_USER_STREAM_INFORMATION musi
;
463 userstream
.Type
= LastReservedStream
+ 1;
464 userstream
.Buffer
= (void*)this->crashlog
;
465 userstream
.BufferSize
= (ULONG
)strlen(this->crashlog
) + 1;
467 musi
.UserStreamCount
= 1;
468 musi
.UserStreamArray
= &userstream
;
470 mdei
.ThreadId
= GetCurrentThreadId();
471 mdei
.ExceptionPointers
= ep
;
472 mdei
.ClientPointers
= false;
474 funcMiniDumpWriteDump(proc
, procid
, file
, MiniDumpWithDataSegs
, &mdei
, &musi
, NULL
);
479 FreeLibrary(dbghelp
);
483 #endif /* _MSC_VER */
485 extern bool CloseConsoleLogIfActive();
486 static void ShowCrashlogWindow();
489 * Stack pointer for use when 'starting' the crash handler.
490 * Not static as gcc's inline assembly needs it that way.
492 void *_safe_esp
= NULL
;
494 static LONG WINAPI
ExceptionHandler(EXCEPTION_POINTERS
*ep
)
496 _in_event_loop_post_crash
= true;
498 if (CrashLogWindows::current
!= NULL
) {
499 CrashLog::AfterCrashLogCleanup();
503 const char *abort_reason
= CrashLog::GetAbortCrashlogReason();
504 if (abort_reason
!= NULL
) {
505 TCHAR _emergency_crash
[512];
506 _sntprintf(_emergency_crash
, lengthof(_emergency_crash
),
507 _T("A serious fault condition occurred in the game. The game will shut down.\n"), OTTD2FS(abort_reason
));
508 MessageBox(NULL
, _emergency_crash
, _T("Fatal Application Failure"), MB_ICONERROR
);
512 CrashLogWindows
*log
= new CrashLogWindows(ep
);
513 CrashLogWindows::current
= log
;
514 char *buf
= log
->FillCrashLog(log
->crashlog
, lastof(log
->crashlog
));
515 log
->WriteCrashDump(log
->crashdump_filename
, lastof(log
->crashdump_filename
));
516 log
->AppendDecodedStacktrace(buf
, lastof(log
->crashlog
));
517 log
->WriteCrashLog(log
->crashlog
, log
->crashlog_filename
, lastof(log
->crashlog_filename
));
518 log
->WriteScreenshot(log
->screenshot_filename
, lastof(log
->screenshot_filename
));
520 /* Close any possible log files */
521 CloseConsoleLogIfActive();
523 if ((VideoDriver::GetInstance() == NULL
|| VideoDriver::GetInstance()->HasGUI()) && _safe_esp
!= NULL
) {
525 ep
->ContextRecord
->Rip
= (DWORD64
)ShowCrashlogWindow
;
526 ep
->ContextRecord
->Rsp
= (DWORD64
)_safe_esp
;
528 ep
->ContextRecord
->Eip
= (DWORD
)ShowCrashlogWindow
;
529 ep
->ContextRecord
->Esp
= (DWORD
)_safe_esp
;
531 return EXCEPTION_CONTINUE_EXECUTION
;
534 CrashLog::AfterCrashLogCleanup();
535 return EXCEPTION_EXECUTE_HANDLER
;
538 static void CDECL
CustomAbort(int signal
)
540 RaiseException(0xE1212012, 0, 0, NULL
);
543 /* static */ void CrashLog::InitialiseCrashLog()
547 RtlCaptureContext(&ctx
);
549 /* The stack pointer for AMD64 must always be 16-byte aligned inside a
550 * function. As we are simulating a function call with the safe ESP value,
551 * we need to subtract 8 for the imaginary return address otherwise stack
552 * alignment would be wrong in the called function. */
553 _safe_esp
= (void *)(ctx
.Rsp
- 8);
555 #if defined(_MSC_VER)
560 asm("movl %esp, __safe_esp");
564 /* SIGABRT is not an unhandled exception, so we need to intercept it. */
565 signal(SIGABRT
, CustomAbort
);
566 #if defined(_MSC_VER)
567 /* Don't show abort message as we will get the crashlog window anyway. */
568 _set_abort_behavior(0, _WRITE_ABORT_MSG
);
570 SetUnhandledExceptionFilter(ExceptionHandler
);
573 /* The crash log GUI */
575 static bool _expanded
;
577 static const TCHAR _crash_desc
[] =
578 _T("A serious fault condition occurred in the game. The game will shut down.\n")
579 _T("Please send the crash information and the crash.dmp file (if any) to the patchpack developer.\n")
580 _T("This will greatly help debugging. The correct place to do this is https://www.tt-forums.net/viewtopic.php?f=33&t=74365")
581 _T(" or https://github.com/KeldorKatarn/OpenTTD_PatchPack\n")
582 _T("The information contained in the report is displayed below.\n")
583 _T("Press \"Emergency save\" to attempt saving the game. Generated file(s):\n")
586 static const TCHAR _save_succeeded
[] =
587 _T("Emergency save succeeded.\nIts location is '%s'.\n")
588 _T("Be aware that critical parts of the internal game state may have become ")
589 _T("corrupted. The saved game is not guaranteed to work.");
591 static const TCHAR
* const _expand_texts
[] = {_T("S&how report >>"), _T("&Hide report <<") };
593 static void SetWndSize(HWND wnd
, int mode
)
597 GetWindowRect(wnd
, &r
);
598 SetDlgItemText(wnd
, 15, _expand_texts
[mode
== 1]);
601 GetWindowRect(GetDlgItem(wnd
, 11), &r2
);
602 int offs
= r2
.bottom
- r2
.top
+ 10;
603 if (mode
== 0) offs
= -offs
;
604 SetWindowPos(wnd
, HWND_TOPMOST
, 0, 0,
605 r
.right
- r
.left
, r
.bottom
- r
.top
+ offs
, SWP_NOMOVE
| SWP_NOZORDER
);
607 SetWindowPos(wnd
, HWND_TOPMOST
,
608 (GetSystemMetrics(SM_CXSCREEN
) - (r
.right
- r
.left
)) / 2,
609 (GetSystemMetrics(SM_CYSCREEN
) - (r
.bottom
- r
.top
)) / 2,
614 /* When TCHAR is char, then _sntprintf becomes snprintf. When TCHAR is wchar it doesn't. Likewise for strcat. */
618 static INT_PTR CALLBACK
CrashDialogFunc(HWND wnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
621 case WM_INITDIALOG
: {
622 /* We need to put the crash-log in a separate buffer because the default
623 * buffer in MB_TO_WIDE is not large enough (512 chars) */
624 TCHAR crash_msgW
[lengthof(CrashLogWindows::current
->crashlog
)];
625 /* Convert unix -> dos newlines because the edit box only supports that properly :( */
626 const char *unix_nl
= CrashLogWindows::current
->crashlog
;
627 char dos_nl
[lengthof(CrashLogWindows::current
->crashlog
)];
630 while ((c
= Utf8Consume(&unix_nl
)) && p
< lastof(dos_nl
) - 4) { // 4 is max number of bytes per character
631 if (c
== '\n') p
+= Utf8Encode(p
, '\r');
632 p
+= Utf8Encode(p
, c
);
636 /* Add path to crash.log and crash.dmp (if any) to the crash window text */
637 size_t len
= _tcslen(_crash_desc
) + 2;
638 len
+= _tcslen(OTTD2FS(CrashLogWindows::current
->crashlog_filename
)) + 2;
639 len
+= _tcslen(OTTD2FS(CrashLogWindows::current
->crashdump_filename
)) + 2;
640 len
+= _tcslen(OTTD2FS(CrashLogWindows::current
->screenshot_filename
)) + 1;
642 TCHAR
*text
= AllocaM(TCHAR
, len
);
643 _sntprintf(text
, len
, _crash_desc
, OTTD2FS(CrashLogWindows::current
->crashlog_filename
));
644 if (OTTD2FS(CrashLogWindows::current
->crashdump_filename
)[0] != _T('\0')) {
645 _tcscat(text
, _T("\n"));
646 _tcscat(text
, OTTD2FS(CrashLogWindows::current
->crashdump_filename
));
648 if (OTTD2FS(CrashLogWindows::current
->screenshot_filename
)[0] != _T('\0')) {
649 _tcscat(text
, _T("\n"));
650 _tcscat(text
, OTTD2FS(CrashLogWindows::current
->screenshot_filename
));
653 SetDlgItemText(wnd
, 10, text
);
654 SetDlgItemText(wnd
, 11, convert_to_fs(dos_nl
, crash_msgW
, lengthof(crash_msgW
)));
655 SendDlgItemMessage(wnd
, 11, WM_SETFONT
, (WPARAM
)GetStockObject(ANSI_FIXED_FONT
), FALSE
);
661 CrashLog::AfterCrashLogCleanup();
663 case 13: // Emergency save
664 char filename
[MAX_PATH
];
665 if (CrashLogWindows::current
->WriteSavegame(filename
, lastof(filename
))) {
666 size_t len
= _tcslen(_save_succeeded
) + _tcslen(OTTD2FS(filename
)) + 1;
667 TCHAR
*text
= AllocaM(TCHAR
, len
);
668 _sntprintf(text
, len
, _save_succeeded
, OTTD2FS(filename
));
669 MessageBox(wnd
, text
, _T("Save successful"), MB_ICONINFORMATION
);
671 MessageBox(wnd
, _T("Save failed"), _T("Save failed"), MB_ICONINFORMATION
);
674 case 15: // Expand window to show crash-message
676 SetWndSize(wnd
, _expanded
);
681 CrashLog::AfterCrashLogCleanup();
688 static void ShowCrashlogWindow()
691 ShowWindow(GetActiveWindow(), FALSE
);
692 DialogBox(GetModuleHandle(NULL
), MAKEINTRESOURCE(100), NULL
, CrashDialogFunc
);