Translations update
[openttd/fttd.git] / src / gamelog.cpp
blobcdbfb05b0ba481a2a52d75d401d888f21c94c93b
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 gamelog.cpp Definition of functions used for logging of important changes in the game */
12 #include "stdafx.h"
13 #include "saveload/saveload.h"
14 #include "string.h"
15 #include "settings_type.h"
16 #include "gamelog_entries.h"
17 #include "console_func.h"
18 #include "debug.h"
19 #include "date_func.h"
20 #include "rev.h"
22 #include <stdarg.h>
25 Gamelog _gamelog; ///< gamelog
27 /**
28 * Resets and frees all memory allocated - used before loading or starting a new game
30 void GamelogReset()
32 _gamelog.clear();
36 /**
37 * Get some basic information from the given gamelog.
38 * @param gamelog Pointer to the gamelog to extract information from.
39 * @param [out] last_rev NewGRF version from the binary that last saved the savegame.
40 * @param [out] ever_modified Max value of 'modified' from all binaries that ever saved this savegame.
41 * @param [out] removed_newgrfs Set to true if any NewGRFs have been removed.
43 void GamelogInfo(const Gamelog *gamelog, uint32 *last_rev, byte *ever_modified, bool *removed_newgrfs)
45 for (Gamelog::const_iterator entry = gamelog->begin(); entry != gamelog->end(); entry++) {
46 switch ((*entry)->type) {
47 default: break;
49 case GLOG_REVISION: {
50 const GamelogEntryRevision *rev = (GamelogEntryRevision*)entry->get();
51 *last_rev = rev->newgrf;
52 *ever_modified = max(*ever_modified, rev->modified);
53 break;
56 case GLOG_GRFREM:
57 *removed_newgrfs = true;
58 break;
64 GamelogEntry *GamelogEntryByType(uint type)
66 switch (type) {
67 case GLOG_START: return new GamelogEntryStart();
68 case GLOG_STARTED: return new GamelogEntryStarted();
69 case GLOG_LOAD: return new GamelogEntryLoad();
70 case GLOG_LOADED: return new GamelogEntryLoaded();
71 case GLOG_MODE: return new GamelogEntryMode();
72 case GLOG_REVISION: return new GamelogEntryRevision();
73 case GLOG_LEGACYREV: return new GamelogEntryLegacyRev();
74 case GLOG_OLDVER: return new GamelogEntryOldVer();
75 case GLOG_EMERGENCY: return new GamelogEntryEmergency();
76 case GLOG_SETTING: return new GamelogEntrySetting();
77 case GLOG_CHEAT: return new GamelogEntryCheat();
78 case GLOG_GRFBEGIN: return new GamelogEntryGRFBegin();
79 case GLOG_GRFEND: return new GamelogEntryGRFEnd();
80 case GLOG_GRFADD: return new GamelogEntryGRFAdd();
81 case GLOG_GRFREM: return new GamelogEntryGRFRemove();
82 case GLOG_GRFCOMPAT: return new GamelogEntryGRFCompat();
83 case GLOG_GRFPARAM: return new GamelogEntryGRFParam();
84 case GLOG_GRFMOVE: return new GamelogEntryGRFMove();
85 case GLOG_GRFBUG: return new GamelogEntryGRFBug();
86 default: NOT_REACHED();
91 /**
92 * Information about the presence of a Grf at a certain point during gamelog history
93 * Note about missing Grfs:
94 * Changes to missing Grfs are not logged including manual removal of the Grf.
95 * So if the gamelog tells a Grf is missing we do not know whether it was readded or completely removed
96 * at some later point.
98 struct GRFPresence{
99 const GRFConfig *gc; ///< GRFConfig, if known
100 bool was_missing; ///< Grf was missing during some gameload in the past
102 GRFPresence(const GRFConfig *gc) : gc(gc), was_missing(false) {}
104 typedef SmallMap<uint32, GRFPresence> GrfIDMapping;
107 /** Gamelog print buffer */
108 struct GamelogPrintBuffer : sstring<1024> {
109 GrfIDMapping grf_names; ///< keep track of this so that inconsistencies can be detected
110 bool in_load; ///< currently printing between GLOG_LOAD and GLOG_LOADED
114 * Prints GRF ID, checksum and filename if found
115 * @param buf The buffer to write to
116 * @param grfid GRF ID
117 * @param md5sum array of md5sum to print, if known
118 * @param gc GrfConfig, if known
120 static void PrintGrfInfo(GamelogPrintBuffer *buf, uint grfid, const uint8 *md5sum, const GRFConfig *gc)
122 if (md5sum != NULL) {
123 char txt [40];
124 md5sumToString (txt, md5sum);
125 buf->append_fmt ("GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
126 } else {
127 buf->append_fmt ("GRF ID %08X", BSWAP32(grfid));
130 if (gc != NULL) {
131 buf->append_fmt (", filename: %s (md5sum matches)", gc->filename);
132 } else {
133 gc = FindGRFConfig(grfid, FGCM_ANY);
134 if (gc != NULL) {
135 buf->append_fmt (", filename: %s (matches GRFID only)", gc->filename);
136 } else {
137 buf->append(", unknown GRF");
143 * Prints active gamelog
144 * @param proc the procedure to draw with
146 void GamelogPrint (GamelogPrintProc *proc, void *data)
148 GamelogPrintBuffer buf;
150 proc ("---- gamelog start ----", data);
152 for (Gamelog::const_iterator entry = _gamelog.begin(); entry != _gamelog.end(); entry++) {
153 buf.clear();
154 (*entry)->Print(&buf);
155 proc (buf.c_str(), data);
158 proc ("---- gamelog end ----", data);
162 static void GamelogPrintConsoleProc (const char *s, void*)
164 IConsolePrint(CC_WARNING, s);
167 /** Print the gamelog data to the console. */
168 void GamelogPrintConsole()
170 GamelogPrint (&GamelogPrintConsoleProc, NULL);
173 static void GamelogPrintDebugProc (const char *s, void *level)
175 DEBUG(gamelog, *(int*)level, "%s", s);
179 * Prints gamelog to debug output. Code is executed even when
180 * there will be no output. It is called very seldom, so it
181 * doesn't matter that much. At least it gives more uniform code...
182 * @param level debug level we need to print stuff
184 void GamelogPrintDebug(int level)
186 GamelogPrint (&GamelogPrintDebugProc, &level);
190 /* Gamelog entry types */
193 /* Gamelog entry base class for entries with tick information. */
195 void GamelogEntryTimed::PrependTick(GamelogPrintBuffer *buf) {
196 buf->append_fmt ("Tick %u: ", (uint)this->tick);
200 /* Gamelog entry for game start */
202 void GamelogEntryStart::Print(GamelogPrintBuffer *buf) {
203 this->PrependTick(buf);
204 buf->append("New game");
207 void GamelogAddStart()
209 _gamelog.append(new GamelogEntryStart());
213 /* Gamelog entry after game start */
215 void GamelogEntryStarted::Print(GamelogPrintBuffer *buf) {
216 buf->append(" Game started");
219 void GamelogAddStarted()
221 _gamelog.append(new GamelogEntryStarted());
225 /** Gamelog entry for game load */
226 void GamelogEntryLoad::Print(GamelogPrintBuffer *buf) {
227 this->PrependTick(buf);
228 buf->append("Load game");
229 buf->in_load = true;
232 void GamelogAddLoad()
234 _gamelog.append(new GamelogEntryLoad());
238 /* Gamelog entry after game load */
240 void GamelogEntryLoaded::Print(GamelogPrintBuffer *buf) {
241 buf->append(" Game loaded");
242 buf->in_load = false;
245 void GamelogAddLoaded()
247 _gamelog.append(new GamelogEntryLoaded());
251 /* Gamelog entry for mode switch between scenario editor and game */
253 void GamelogEntryMode::Print(GamelogPrintBuffer *buf) {
254 buf->append_fmt (" New game mode %u, landscape %u",
255 (uint)this->mode, (uint)this->landscape);
259 * Logs a change in game mode (scenario editor or game)
261 void GamelogAddMode()
263 _gamelog.append(new GamelogEntryMode());
267 * Finds last stored game mode or landscape.
268 * Any change is logged
270 void GamelogTestMode()
272 const GamelogEntryMode *mode = NULL;
274 for (Gamelog::const_iterator entry = _gamelog.begin(); entry != _gamelog.end(); entry++) {
275 if ((*entry)->type == GLOG_MODE) {
276 mode = (GamelogEntryMode*)entry->get();
280 if (mode == NULL || mode->mode != _game_mode || mode->landscape != _settings_game.game_creation.landscape) GamelogAddMode();
284 /* Gamelog entry for game revision string */
286 void GamelogEntryRevision::Print(GamelogPrintBuffer *buf) {
287 buf->append_fmt (" Revision text changed to %s, savegame version %u, %smodified, newgrf version 0x%08x",
288 this->text, this->slver,
289 (this->modified == 0) ? "not " : (this->modified == 1) ? "maybe " : "",
290 this->newgrf);
294 * Logs a change in game revision
296 void GamelogAddRevision()
298 _gamelog.append(new GamelogEntryRevision());
302 * Finds out if current revision is different than last revision stored in the savegame.
303 * Appends a revision entry when the revision string changed
305 void GamelogTestRevision()
307 const GamelogEntryRevision *rev = NULL;
309 for (Gamelog::const_iterator entry = _gamelog.begin(); entry != _gamelog.end(); entry++) {
310 if ((*entry)->type == GLOG_REVISION) {
311 rev = (GamelogEntryRevision*)entry->get();
315 if (rev == NULL || strcmp(rev->text, _openttd_revision) != 0 ||
316 rev->modified != _openttd_revision_modified ||
317 rev->newgrf != _openttd_newgrf_version) {
318 GamelogAddRevision();
323 /* Gamelog entry for game revision string (legacy) */
325 void GamelogEntryLegacyRev::Print(GamelogPrintBuffer *buf) {
326 buf->append_fmt (" Revision text changed to %s (legacy), savegame version %u, %smodified, newgrf version 0x%08x",
327 this->text, this->slver,
328 (this->modified == 0) ? "not " : (this->modified == 1) ? "maybe " : "",
329 this->newgrf);
333 /* Gamelog entry for savegames without log */
335 void GamelogEntryOldVer::Print(GamelogPrintBuffer *buf) {
336 switch (this->savetype) {
337 case SGT_TTO:
338 buf->append(" Conversion from TTO savegame");
339 break;
341 case SGT_TTD:
342 buf->append(" Conversion from TTD savegame");
343 break;
345 case SGT_TTDP1:
346 case SGT_TTDP2:
347 buf->append_fmt (" Conversion from %s TTDP savegame version %u.%u.%u.%u",
348 (this->savetype == SGT_TTDP1) ? "old" : "new",
349 GB(this->version, 24, 8),
350 GB(this->version, 20, 4),
351 GB(this->version, 16, 4),
352 GB(this->version, 0, 16));
353 break;
355 default: NOT_REACHED();
356 case SGT_OTTD:
357 buf->append_fmt (" Conversion from OTTD savegame without gamelog, version %u, %u",
358 GB(this->version, 8, 16), GB(this->version, 0, 8));
359 break;
364 * Logs loading from savegame without gamelog
366 void GamelogOldver(const SavegameTypeVersion *stv)
368 _gamelog.append(new GamelogEntryOldVer(stv));
372 /* Gamelog entry for emergency savegames. */
374 void GamelogEntryEmergency::Print(GamelogPrintBuffer *buf) {
375 this->PrependTick(buf);
376 buf->append("Emergency savegame");
380 * Logs an emergency savegame
382 void GamelogEmergency()
384 _gamelog.append(new GamelogEntryEmergency());
388 * Finds out if current game is a loaded emergency savegame.
390 bool GamelogTestEmergency()
392 for (Gamelog::const_iterator entry = _gamelog.begin(); entry != _gamelog.end(); entry++) {
393 if ((*entry)->type == GLOG_EMERGENCY) return true;
396 return false;
400 /* Gamelog entry for settings change */
402 void GamelogEntrySetting::Print(GamelogPrintBuffer *buf) {
403 this->PrependTick(buf);
404 buf->append_fmt ("Setting '%s' changed from %d to %d",
405 this->name, this->oldval, this->newval);
409 * Logs change in game settings. Only non-networksafe settings are logged
410 * @param name setting name
411 * @param oldval old setting value
412 * @param newval new setting value
414 void GamelogSetting(const char *name, int32 oldval, int32 newval)
416 _gamelog.append(new GamelogEntrySetting(name, oldval, newval));
420 /* Gamelog entry for cheating */
422 void GamelogEntryCheat::Print(GamelogPrintBuffer *buf) {
423 this->PrependTick(buf);
424 buf->append("Cheat used");
428 /* Gamelog entry for GRF config change begin */
430 void GamelogEntryGRFBegin::Print(GamelogPrintBuffer *buf) {
431 this->PrependTick(buf);
432 buf->append("GRF config change");
436 * Log GRF config change begin
438 void GamelogGRFBegin()
440 _gamelog.append(new GamelogEntryGRFBegin());
444 /* Gamelog entry for GRF config change end */
446 void GamelogEntryGRFEnd::Print(GamelogPrintBuffer *buf) {
447 buf->append(" GRF config change end");
451 * Log GRF config change end
453 void GamelogGRFEnd()
455 _gamelog.append(new GamelogEntryGRFEnd());
460 * Decides if GRF should be logged
461 * @param g grf to determine
462 * @return true iff GRF is not static and is loaded
464 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
466 return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
470 /* Gamelog entry for GRF addition */
472 void GamelogEntryGRFAdd::Print(GamelogPrintBuffer *buf) {
473 const GRFConfig *gc = FindGRFConfig(this->grf.grfid, FGCM_EXACT, this->grf.md5sum);
474 buf->append(" Added NewGRF: ");
475 PrintGrfInfo(buf, this->grf.grfid, this->grf.md5sum, gc);
476 GrfIDMapping::Pair *gm = buf->grf_names.Find(this->grf.grfid);
477 if (gm != buf->grf_names.End() && !gm->second.was_missing) buf->append(" (inconsistency: already added)");
478 buf->grf_names[this->grf.grfid] = gc;
482 * Logs adding of a GRF
483 * @param newg added GRF
485 void GamelogGRFAdd(const GRFConfig *newg)
487 if (!IsLoggableGrfConfig(newg)) return;
489 _gamelog.append(new GamelogEntryGRFAdd(&newg->ident));
493 * Logs adding of list of GRFs.
494 * Useful when old savegame is loaded or when new game is started
495 * @param newg head of GRF linked list
497 void GamelogGRFAddList(const GRFConfig *newg)
499 for (; newg != NULL; newg = newg->next) {
500 GamelogGRFAdd(newg);
505 /* Gamelog entry for GRF removal */
507 void GamelogEntryGRFRemove::Print(GamelogPrintBuffer *buf) {
508 GrfIDMapping::Pair *gm = buf->grf_names.Find(this->grfid);
509 buf->append(buf->in_load ? " Missing NewGRF: " : " Removed NewGRF: ");
510 PrintGrfInfo(buf, this->grfid, NULL, gm != buf->grf_names.End() ? gm->second.gc : NULL);
511 if (gm == buf->grf_names.End()) {
512 buf->append(" (inconsistency: never added)");
513 } else if (buf->in_load) {
514 /* Missing grfs on load are not removed from the configuration */
515 gm->second.was_missing = true;
516 } else {
517 buf->grf_names.Erase(gm);
522 * Logs removal of a GRF
523 * @param grfid ID of removed GRF
525 void GamelogGRFRemove(uint32 grfid)
527 _gamelog.append(new GamelogEntryGRFRemove(grfid));
531 /* Gamelog entry for compatible GRF load */
533 void GamelogEntryGRFCompat::Print(GamelogPrintBuffer *buf) {
534 const GRFConfig *gc = FindGRFConfig(this->grf.grfid, FGCM_EXACT, this->grf.md5sum);
535 buf->append(" Compatible NewGRF loaded: ");
536 PrintGrfInfo(buf, this->grf.grfid, this->grf.md5sum, gc);
537 if (!buf->grf_names.Contains(this->grf.grfid)) buf->append(" (inconsistency: never added)");
538 buf->grf_names[this->grf.grfid] = gc;
542 * Logs loading compatible GRF
543 * (the same ID, but different MD5 hash)
544 * @param newg new (updated) GRF
546 void GamelogGRFCompatible(const GRFIdentifier *newg)
548 _gamelog.append(new GamelogEntryGRFCompat(newg));
552 /* Gamelog entry for GRF parameter changes */
554 void GamelogEntryGRFParam::Print(GamelogPrintBuffer *buf) {
555 GrfIDMapping::Pair *gm = buf->grf_names.Find(this->grfid);
556 buf->append(" GRF parameter changed: ");
557 PrintGrfInfo(buf, this->grfid, NULL, gm != buf->grf_names.End() ? gm->second.gc : NULL);
558 if (gm == buf->grf_names.End()) buf->append(" (inconsistency: never added)");
562 * Logs change in GRF parameters.
563 * Details about parameters changed are not stored
564 * @param grfid ID of GRF to store
566 static void GamelogGRFParameters(uint32 grfid)
568 _gamelog.append(new GamelogEntryGRFParam(grfid));
572 /* Gamelog entry for GRF order change */
574 void GamelogEntryGRFMove::Print(GamelogPrintBuffer *buf) {
575 GrfIDMapping::Pair *gm = buf->grf_names.Find(this->grfid);
576 buf->append_fmt ("GRF order changed: %08X moved %d places %s, ",
577 BSWAP32(this->grfid), abs(this->offset), this->offset >= 0 ? "down" : "up");
578 PrintGrfInfo(buf, this->grfid, NULL, gm != buf->grf_names.End() ? gm->second.gc : NULL);
579 if (gm == buf->grf_names.End()) buf->append(" (inconsistency: never added)");
583 * Logs changing GRF order
584 * @param grfid GRF that is moved
585 * @param offset how far it is moved, positive = moved down
587 static void GamelogGRFMove(uint32 grfid, int32 offset)
589 _gamelog.append(new GamelogEntryGRFMove(grfid, offset));
593 /** List of GRFs using array of pointers instead of linked list */
594 struct GRFList {
595 uint n;
596 const GRFConfig *grf[];
600 * Generates GRFList
601 * @param grfc head of GRF linked list
603 static GRFList *GenerateGRFList(const GRFConfig *grfc)
605 uint n = 0;
606 for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
607 if (IsLoggableGrfConfig(g)) n++;
610 GRFList *list = (GRFList*) xmalloc (sizeof(GRFList) + n * sizeof(GRFConfig*));
612 list->n = 0;
613 for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
614 if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
617 return list;
621 * Compares two NewGRF lists and logs any change
622 * @param oldc original GRF list
623 * @param newc new GRF list
625 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
627 GRFList *ol = GenerateGRFList(oldc);
628 GRFList *nl = GenerateGRFList(newc);
630 uint o = 0, n = 0;
632 while (o < ol->n && n < nl->n) {
633 const GRFConfig *og = ol->grf[o];
634 const GRFConfig *ng = nl->grf[n];
636 if (og->ident.grfid != ng->ident.grfid) {
637 uint oi, ni;
638 for (oi = 0; oi < ol->n; oi++) {
639 if (ol->grf[oi]->ident.grfid == nl->grf[n]->ident.grfid) break;
641 if (oi < o) {
642 /* GRF was moved, this change has been logged already */
643 n++;
644 continue;
646 if (oi == ol->n) {
647 /* GRF couldn't be found in the OLD list, GRF was ADDED */
648 GamelogGRFAdd(nl->grf[n++]);
649 continue;
651 for (ni = 0; ni < nl->n; ni++) {
652 if (nl->grf[ni]->ident.grfid == ol->grf[o]->ident.grfid) break;
654 if (ni < n) {
655 /* GRF was moved, this change has been logged already */
656 o++;
657 continue;
659 if (ni == nl->n) {
660 /* GRF couldn't be found in the NEW list, GRF was REMOVED */
661 GamelogGRFRemove(ol->grf[o++]->ident.grfid);
662 continue;
665 /* o < oi < ol->n
666 * n < ni < nl->n */
667 assert(ni > n && ni < nl->n);
668 assert(oi > o && oi < ol->n);
670 ni -= n; // number of GRFs it was moved downwards
671 oi -= o; // number of GRFs it was moved upwards
673 if (ni >= oi) { // prefer the one that is moved further
674 /* GRF was moved down */
675 GamelogGRFMove(ol->grf[o++]->ident.grfid, ni);
676 } else {
677 GamelogGRFMove(nl->grf[n++]->ident.grfid, -(int)oi);
679 } else {
680 if (memcmp(og->ident.md5sum, ng->ident.md5sum, sizeof(og->ident.md5sum)) != 0) {
681 /* md5sum changed, probably loading 'compatible' GRF */
682 GamelogGRFCompatible(&nl->grf[n]->ident);
685 if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
686 GamelogGRFParameters(ol->grf[o]->ident.grfid);
689 o++;
690 n++;
694 while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->ident.grfid); // remaining GRFs were removed ...
695 while (n < nl->n) GamelogGRFAdd (nl->grf[n++]); // ... or added
697 free(ol);
698 free(nl);
702 /* Gamelog entry for GRF bugs */
704 void GamelogEntryGRFBug::Print(GamelogPrintBuffer *buf) {
705 this->PrependTick(buf);
706 GrfIDMapping::Pair *gm = buf->grf_names.Find(this->grfid);
707 switch (this->bug) {
708 default: NOT_REACHED();
709 case GBUG_VEH_LENGTH:
710 buf->append_fmt ("Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(this->grfid), (uint)this->data);
711 break;
713 PrintGrfInfo(buf, this->grfid, NULL, gm != buf->grf_names.End() ? gm->second.gc : NULL);
714 if (gm == buf->grf_names.End()) buf->append(" (inconsistency: never added)");
718 * Logs triggered GRF bug.
719 * @param grfid ID of problematic GRF
720 * @param bug type of bug, @see enum GRFBugs
721 * @param data additional data
723 static inline void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
725 _gamelog.append(new GamelogEntryGRFBug(grfid, bug, data));
729 * Logs GRF bug - rail vehicle has different length after reversing.
730 * Ensures this is logged only once for each GRF and engine type
731 * This check takes some time, but it is called pretty seldom, so it
732 * doesn't matter that much (ideally it shouldn't be called at all).
733 * @param grfid the broken NewGRF
734 * @param internal_id the internal ID of whatever's broken in the NewGRF
735 * @return true iff a unique record was done
737 bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
739 for (Gamelog::const_iterator entry = _gamelog.begin(); entry != _gamelog.end(); entry++) {
740 if ((*entry)->type == GLOG_GRFBUG) {
741 const GamelogEntryGRFBug *bug = (GamelogEntryGRFBug*)entry->get();
742 if (bug->bug == GBUG_VEH_LENGTH && bug->grfid == grfid && bug->data == internal_id) {
743 return false;
748 GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
750 return true;