Add ScriptStoryPageElementList to get the contents of a page
[openttd/fttd.git] / src / crashlog.cpp
blob761c479485414b5a01033924bc1c9e5c65746161
1 /* $Id$ */
3 /*
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/>.
8 */
10 /** @file crashlog.cpp Implementation of generic function to be called to log a crash */
12 #include "stdafx.h"
13 #include "crashlog.h"
14 #include "gamelog.h"
15 #include "date_func.h"
16 #include "rev.h"
17 #include "strings_func.h"
18 #include "blitter/factory.hpp"
19 #include "base_media_base.h"
20 #include "music/music_driver.hpp"
21 #include "sound/sound_driver.hpp"
22 #include "video/video_driver.hpp"
23 #include "saveload/saveload.h"
24 #include "screenshot.h"
25 #include "gfx_func.h"
26 #include "network/network.h"
27 #include "language.h"
28 #include "fontcache.h"
30 #include "ai/ai_info.hpp"
31 #include "game/game.hpp"
32 #include "game/game_info.hpp"
33 #include "company_base.h"
34 #include "company_func.h"
36 #include <time.h>
38 /* static */ const char *CrashLog::message = NULL;
39 /* static */ char *CrashLog::gamelog_buffer = NULL;
40 /* static */ const char *CrashLog::gamelog_last = NULL;
42 char *CrashLog::LogCompiler(char *buffer, const char *last) const
44 buffer += seprintf(buffer, last, " Compiler: "
45 #if defined(_MSC_VER)
46 "MSVC %d", _MSC_VER
47 #elif defined(__ICC) && defined(__GNUC__)
48 "ICC %d (GCC %d.%d.%d mode)", __ICC, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
49 #elif defined(__ICC)
50 "ICC %d", __ICC
51 #elif defined(__GNUC__)
52 "GCC %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
53 #elif defined(__WATCOMC__)
54 "WatcomC %d", __WATCOMC__
55 #else
56 "<unknown>"
57 #endif
59 #if defined(__VERSION__)
60 return buffer + seprintf(buffer, last, " \"" __VERSION__ "\"\n\n");
61 #else
62 return buffer + seprintf(buffer, last, "\n\n");
63 #endif
66 /* virtual */ char *CrashLog::LogRegisters(char *buffer, const char *last) const
68 /* Stub implementation; not all OSes support this. */
69 return buffer;
72 /* virtual */ char *CrashLog::LogModules(char *buffer, const char *last) const
74 /* Stub implementation; not all OSes support this. */
75 return buffer;
78 /**
79 * Writes OpenTTD's version to the buffer.
80 * @param buffer The begin where to write at.
81 * @param last The last position in the buffer to write to.
82 * @return the position of the \c '\0' character after the buffer.
84 char *CrashLog::LogOpenTTDVersion(char *buffer, const char *last) const
86 return buffer + seprintf(buffer, last,
87 "OpenTTD version:\n"
88 " Version: %s (%d)\n"
89 " NewGRF ver: %08x\n"
90 " Bits: %d\n"
91 " Endian: %s\n"
92 " Dedicated: %s\n"
93 " Build date: %s\n\n",
94 _openttd_revision,
95 _openttd_revision_modified,
96 _openttd_newgrf_version,
97 #ifdef _SQ64
98 64,
99 #else
101 #endif
102 #if (TTD_ENDIAN == TTD_LITTLE_ENDIAN)
103 "little",
104 #else
105 "big",
106 #endif
107 #ifdef DEDICATED
108 "yes",
109 #else
110 "no",
111 #endif
112 _openttd_build_date
117 * Writes the (important) configuration settings to the buffer.
118 * E.g. graphics set, sound set, blitter and AIs.
119 * @param buffer The begin where to write at.
120 * @param last The last position in the buffer to write to.
121 * @return the position of the \c '\0' character after the buffer.
123 char *CrashLog::LogConfiguration(char *buffer, const char *last) const
125 buffer += seprintf(buffer, last,
126 "Configuration:\n"
127 " Blitter: %s\n"
128 " Graphics set: %s (%u)\n"
129 " Language: %s\n"
130 " Music driver: %s\n"
131 " Music set: %s (%u)\n"
132 " Network: %s\n"
133 " Sound driver: %s\n"
134 " Sound set: %s (%u)\n"
135 " Video driver: %s\n\n",
136 BlitterFactory::GetCurrentBlitter() == NULL ? "none" : BlitterFactory::GetCurrentBlitter()->GetName(),
137 BaseGraphics::GetUsedSet() == NULL ? "none" : BaseGraphics::GetUsedSet()->name,
138 BaseGraphics::GetUsedSet() == NULL ? UINT32_MAX : BaseGraphics::GetUsedSet()->version,
139 _current_language == NULL ? "none" : _current_language->file,
140 _music_driver == NULL ? "none" : _music_driver->GetName(),
141 BaseMusic::GetUsedSet() == NULL ? "none" : BaseMusic::GetUsedSet()->name,
142 BaseMusic::GetUsedSet() == NULL ? UINT32_MAX : BaseMusic::GetUsedSet()->version,
143 _networking ? (_network_server ? "server" : "client") : "no",
144 _sound_driver == NULL ? "none" : _sound_driver->GetName(),
145 BaseSounds::GetUsedSet() == NULL ? "none" : BaseSounds::GetUsedSet()->name,
146 BaseSounds::GetUsedSet() == NULL ? UINT32_MAX : BaseSounds::GetUsedSet()->version,
147 _video_driver == NULL ? "none" : _video_driver->GetName()
150 buffer += seprintf(buffer, last,
151 "Fonts:\n"
152 " Small: %s\n"
153 " Medium: %s\n"
154 " Large: %s\n"
155 " Mono: %s\n\n",
156 FontCache::Get(FS_SMALL)->GetFontName(),
157 FontCache::Get(FS_NORMAL)->GetFontName(),
158 FontCache::Get(FS_LARGE)->GetFontName(),
159 FontCache::Get(FS_MONO)->GetFontName()
162 buffer += seprintf(buffer, last, "AI Configuration (local: %i):\n", (int)_local_company);
163 const Company *c;
164 FOR_ALL_COMPANIES(c) {
165 if (c->ai_info == NULL) {
166 buffer += seprintf(buffer, last, " %2i: Human\n", (int)c->index);
167 } else {
168 buffer += seprintf(buffer, last, " %2i: %s (v%d)\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
172 if (Game::GetInfo() != NULL) {
173 buffer += seprintf(buffer, last, " GS: %s (v%d)\n", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
175 buffer += seprintf(buffer, last, "\n");
177 return buffer;
180 /* Include these here so it's close to where it's actually used. */
181 #ifdef WITH_ALLEGRO
182 # include <allegro.h>
183 #endif /* WITH_ALLEGRO */
184 #ifdef WITH_FONTCONFIG
185 # include <fontconfig/fontconfig.h>
186 #endif /* WITH_FONTCONFIG */
187 #ifdef WITH_PNG
188 /* pngconf.h, included by png.h doesn't like something in the
189 * freetype headers. As such it's not alphabetically sorted. */
190 # include <png.h>
191 #endif /* WITH_PNG */
192 #ifdef WITH_FREETYPE
193 # include <ft2build.h>
194 # include FT_FREETYPE_H
195 #endif /* WITH_FREETYPE */
196 #ifdef WITH_ICU
197 # include <unicode/uversion.h>
198 #endif /* WITH_ICU */
199 #ifdef WITH_LZMA
200 # include <lzma.h>
201 #endif
202 #ifdef WITH_LZO
203 #include <lzo/lzo1x.h>
204 #endif
205 #ifdef WITH_SDL
206 # include "sdl.h"
207 # include <SDL.h>
208 #endif /* WITH_SDL */
209 #ifdef WITH_ZLIB
210 # include <zlib.h>
211 #endif
214 * Writes information (versions) of the used libraries.
215 * @param buffer The begin where to write at.
216 * @param last The last position in the buffer to write to.
217 * @return the position of the \c '\0' character after the buffer.
219 char *CrashLog::LogLibraries(char *buffer, const char *last) const
221 buffer += seprintf(buffer, last, "Libraries:\n");
223 #ifdef WITH_ALLEGRO
224 buffer += seprintf(buffer, last, " Allegro: %s\n", allegro_id);
225 #endif /* WITH_ALLEGRO */
227 #ifdef WITH_FONTCONFIG
228 int version = FcGetVersion();
229 buffer += seprintf(buffer, last, " FontConfig: %d.%d.%d\n", version / 10000, (version / 100) % 100, version % 100);
230 #endif /* WITH_FONTCONFIG */
232 #ifdef WITH_FREETYPE
233 FT_Library library;
234 int major, minor, patch;
235 FT_Init_FreeType(&library);
236 FT_Library_Version(library, &major, &minor, &patch);
237 FT_Done_FreeType(library);
238 buffer += seprintf(buffer, last, " FreeType: %d.%d.%d\n", major, minor, patch);
239 #endif /* WITH_FREETYPE */
241 #ifdef WITH_ICU
242 /* 4 times 0-255, separated by dots (.) and a trailing '\0' */
243 char buf[4 * 3 + 3 + 1];
244 UVersionInfo ver;
245 u_getVersion(ver);
246 u_versionToString(ver, buf);
247 buffer += seprintf(buffer, last, " ICU: %s\n", buf);
248 #endif /* WITH_ICU */
250 #ifdef WITH_LZMA
251 buffer += seprintf(buffer, last, " LZMA: %s\n", lzma_version_string());
252 #endif
254 #ifdef WITH_LZO
255 buffer += seprintf(buffer, last, " LZO: %s\n", lzo_version_string());
256 #endif
258 #ifdef WITH_PNG
259 buffer += seprintf(buffer, last, " PNG: %s\n", png_get_libpng_ver(NULL));
260 #endif /* WITH_PNG */
262 #ifdef WITH_SDL
263 #ifdef DYNAMICALLY_LOADED_SDL
264 if (SDL_CALL SDL_Linked_Version != NULL) {
265 #else
267 #endif
268 const SDL_version *v = SDL_CALL SDL_Linked_Version();
269 buffer += seprintf(buffer, last, " SDL: %d.%d.%d\n", v->major, v->minor, v->patch);
271 #endif /* WITH_SDL */
273 #ifdef WITH_ZLIB
274 buffer += seprintf(buffer, last, " Zlib: %s\n", zlibVersion());
275 #endif
277 buffer += seprintf(buffer, last, "\n");
278 return buffer;
282 * Helper function for printing the gamelog.
283 * @param s the string to print.
285 /* static */ void CrashLog::GamelogFillCrashLog(const char *s)
287 CrashLog::gamelog_buffer += seprintf(CrashLog::gamelog_buffer, CrashLog::gamelog_last, "%s\n", s);
291 * Writes the gamelog data to the buffer.
292 * @param buffer The begin where to write at.
293 * @param last The last position in the buffer to write to.
294 * @return the position of the \c '\0' character after the buffer.
296 char *CrashLog::LogGamelog(char *buffer, const char *last) const
298 CrashLog::gamelog_buffer = buffer;
299 CrashLog::gamelog_last = last;
300 GamelogPrint(&CrashLog::GamelogFillCrashLog);
301 return CrashLog::gamelog_buffer + seprintf(CrashLog::gamelog_buffer, last, "\n");
305 * Fill the crash log buffer with all data of a crash log.
306 * @param buffer The begin where to write at.
307 * @param last The last position in the buffer to write to.
308 * @return the position of the \c '\0' character after the buffer.
310 char *CrashLog::FillCrashLog(char *buffer, const char *last) const
312 time_t cur_time = time(NULL);
313 buffer += seprintf(buffer, last, "*** OpenTTD Crash Report ***\n\n");
314 buffer += seprintf(buffer, last, "Crash at: %s", asctime(gmtime(&cur_time)));
316 YearMonthDay ymd;
317 ConvertDateToYMD(_date, &ymd);
318 buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i)\n\n", ymd.year, ymd.month + 1, ymd.day, _date_fract);
320 buffer = this->LogError(buffer, last, CrashLog::message);
321 buffer = this->LogOpenTTDVersion(buffer, last);
322 buffer = this->LogRegisters(buffer, last);
323 buffer = this->LogStacktrace(buffer, last);
324 buffer = this->LogOSVersion(buffer, last);
325 buffer = this->LogCompiler(buffer, last);
326 buffer = this->LogConfiguration(buffer, last);
327 buffer = this->LogLibraries(buffer, last);
328 buffer = this->LogModules(buffer, last);
329 buffer = this->LogGamelog(buffer, last);
331 buffer += seprintf(buffer, last, "*** End of OpenTTD Crash Report ***\n");
332 return buffer;
336 * Write the crash log to a file.
337 * @note On success the filename will be filled with the full path of the
338 * crash log file. Make sure filename is at least \c MAX_PATH big.
339 * @param buffer The begin of the buffer to write to the disk.
340 * @param filename Output for the filename of the written file.
341 * @param filename_last The last position in the filename buffer.
342 * @return true when the crash log was successfully written.
344 bool CrashLog::WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const
346 seprintf(filename, filename_last, "%scrash.log", _personal_dir);
348 FILE *file = FioFOpenFile(filename, "w", NO_DIRECTORY);
349 if (file == NULL) return false;
351 size_t len = strlen(buffer);
352 size_t written = fwrite(buffer, 1, len, file);
354 FioFCloseFile(file);
355 return len == written;
358 /* virtual */ int CrashLog::WriteCrashDump(char *filename, const char *filename_last) const
360 /* Stub implementation; not all OSes support this. */
361 return 0;
365 * Write the (crash) savegame to a file.
366 * @note On success the filename will be filled with the full path of the
367 * crash save file. Make sure filename is at least \c MAX_PATH big.
368 * @param filename Output for the filename of the written file.
369 * @param filename_last The last position in the filename buffer.
370 * @return true when the crash save was successfully made.
372 bool CrashLog::WriteSavegame(char *filename, const char *filename_last) const
374 /* If the map array doesn't exist, saving will fail too. If the map got
375 * initialised, there is a big chance the rest is initialised too. */
376 if (_mth == NULL) return false;
378 try {
379 GamelogEmergency();
381 seprintf(filename, filename_last, "%scrash.sav", _personal_dir);
383 /* Don't do a threaded saveload. */
384 return SaveGame(filename, NO_DIRECTORY, false);
385 } catch (...) {
386 return false;
391 * Write the (crash) screenshot to a file.
392 * @note On success the filename will be filled with the full path of the
393 * screenshot. Make sure filename is at least \c MAX_PATH big.
394 * @param filename Output for the filename of the written file.
395 * @param filename_last The last position in the filename buffer.
396 * @return true when the crash screenshot was successfully made.
398 bool CrashLog::WriteScreenshot(char *filename, const char *filename_last) const
400 /* Don't draw when we have invalid screen size */
401 if (_screen.width < 1 || _screen.height < 1 || _screen.dst_ptr == NULL) return false;
403 bool res = MakeScreenshot(SC_CRASHLOG, "crash");
404 if (res) strecpy(filename, _full_screenshot_name, filename_last);
405 return res;
409 * Makes the crash log, writes it to a file and then subsequently tries
410 * to make a crash dump and crash savegame. It uses DEBUG to write
411 * information like paths to the console.
412 * @return true when everything is made successfully.
414 bool CrashLog::MakeCrashLog() const
416 /* Don't keep looping logging crashes. */
417 static bool crashlogged = false;
418 if (crashlogged) return false;
419 crashlogged = true;
421 char filename[MAX_PATH];
422 char buffer[65536];
423 bool ret = true;
425 printf("Crash encountered, generating crash log...\n");
426 this->FillCrashLog(buffer, lastof(buffer));
427 printf("%s\n", buffer);
428 printf("Crash log generated.\n\n");
430 printf("Writing crash log to disk...\n");
431 bool bret = this->WriteCrashLog(buffer, filename, lastof(filename));
432 if (bret) {
433 printf("Crash log written to %s. Please add this file to any bug reports.\n\n", filename);
434 } else {
435 printf("Writing crash log failed. Please attach the output above to any bug reports.\n\n");
436 ret = false;
439 /* Don't mention writing crash dumps because not all platforms support it. */
440 int dret = this->WriteCrashDump(filename, lastof(filename));
441 if (dret < 0) {
442 printf("Writing crash dump failed.\n\n");
443 ret = false;
444 } else if (dret > 0) {
445 printf("Crash dump written to %s. Please add this file to any bug reports.\n\n", filename);
448 printf("Writing crash savegame...\n");
449 bret = this->WriteSavegame(filename, lastof(filename));
450 if (bret) {
451 printf("Crash savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename);
452 } else {
453 ret = false;
454 printf("Writing crash savegame failed. Please attach the last (auto)save to any bug reports.\n\n");
457 printf("Writing crash screenshot...\n");
458 bret = this->WriteScreenshot(filename, lastof(filename));
459 if (bret) {
460 printf("Crash screenshot written to %s. Please add this file to any bug reports.\n\n", filename);
461 } else {
462 ret = false;
463 printf("Writing crash screenshot failed.\n\n");
466 return ret;
470 * Sets a message for the error message handler.
471 * @param message The error message of the error.
473 /* static */ void CrashLog::SetErrorMessage(const char *message)
475 CrashLog::message = message;
479 * Try to close the sound/video stuff so it doesn't keep lingering around
480 * incorrect video states or so, e.g. keeping dpmi disabled.
482 /* static */ void CrashLog::AfterCrashLogCleanup()
484 if (_music_driver != NULL) _music_driver->Stop();
485 if (_sound_driver != NULL) _sound_driver->Stop();
486 if (_video_driver != NULL) _video_driver->Stop();