1
/* $Id: gamelog.cpp 15904 2009-03-30 23:22:13Z rubidium $ */
3
/** @file gamelog.cpp Definition of functions used for logging of important changes in the game */
7
#include "saveload/saveload.h"
8
#include "core/alloc_func.hpp"
10
#include "string_func.h"
11
#include "settings_type.h"
13
#include "gamelog_internal.h"
14
#include "console_func.h"
20
extern const uint16 SAVEGAME_VERSION; ///< current savegame version
22
extern SavegameType _savegame_type; ///< type of savegame we are loading
24
extern uint32 _ttdp_version; ///< version of TTDP savegame (if applicable)
25
extern uint16 _sl_version; ///< the major savegame version identifier
26
extern byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
29
static GamelogActionType _gamelog_action_type = GLAT_NONE; ///< action to record if anything changes
31
LoggedAction *_gamelog_action = NULL; ///< first logged action
32
uint _gamelog_actions = 0; ///< number of actions
33
static LoggedAction *_current_action = NULL; ///< current action we are logging, NULL when there is no action active
36
/** Stores information about new action, but doesn't allocate it
37
* Action is allocated only when there is at least one change
38
* @param at type of action
40
void GamelogStartAction(GamelogActionType at)
42
assert(_gamelog_action_type == GLAT_NONE); // do not allow starting new action without stopping the previous first
43
_gamelog_action_type = at;
46
/** Stops logging of any changes
48
void GamelogStopAction()
50
assert(_gamelog_action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress
52
bool print = _current_action != NULL;
54
_current_action = NULL;
55
_gamelog_action_type = GLAT_NONE;
57
if (print) GamelogPrintDebug(5);
60
/** Resets and frees all memory allocated - used before loading or starting a new game
64
assert(_gamelog_action_type == GLAT_NONE);
66
for (uint i = 0; i < _gamelog_actions; i++) {
67
const LoggedAction *la = &_gamelog_action[i];
68
for (uint j = 0; j < la->changes; j++) {
69
const LoggedChange *lc = &la->change[j];
70
if (lc->ct == GLCT_SETTING) free(lc->setting.name);
75
free(_gamelog_action);
77
_gamelog_action = NULL;
79
_current_action = NULL;
83
GAMELOG_BUF_LEN = 1024 ///< length of buffer for one line of text
86
static int _dbgofs = 0; ///< offset in current output buffer
88
static void AddDebugText(char *buf, const char *s, ...)
90
if (GAMELOG_BUF_LEN <= _dbgofs) return;
95
_dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
100
/** Prints GRF filename if found
101
* @param grfid GRF which filename to print
103
static void PrintGrfFilename(char *buf, uint grfid)
105
const GRFConfig *gc = FindGRFConfig(grfid);
107
if (gc == NULL) return;
109
AddDebugText(buf, ", filename: %s", gc->filename);
112
/** Prints GRF ID, checksum and filename if found
113
* @param grfid GRF ID
114
* @param md5sum array of md5sum to print
116
static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum)
120
md5sumToString(txt, lastof(txt), md5sum);
122
AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
124
PrintGrfFilename(buf, grfid);
130
/** Text messages for various logged actions */
131
static const char *la_text[] = {
134
"GRF config changed",
138
"emergency savegame",
141
assert_compile(lengthof(la_text) == GLAT_END);
144
/** Prints active gamelog */
145
void GamelogPrint(GamelogPrintProc *proc)
147
char buf[GAMELOG_BUF_LEN];
149
proc("---- gamelog start ----");
151
const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
153
for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
154
assert((uint)la->at < GLAT_END);
156
snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
159
const LoggedChange *lcend = &la->change[la->changes];
161
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
163
AddDebugText(buf, " ");
166
default: NOT_REACHED();
168
AddDebugText(buf, "New game mode: %u landscape: %u",
169
(uint)lc->mode.mode, (uint)lc->mode.landscape);
173
AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
174
lc->revision.text, lc->revision.slver);
176
switch (lc->revision.modified) {
177
case 0: AddDebugText(buf, "not "); break;
178
case 1: AddDebugText(buf, "maybe "); break;
182
AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
186
AddDebugText(buf, "Conversion from ");
187
switch (lc->oldver.type) {
188
default: NOT_REACHED();
190
AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
191
GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
195
AddDebugText(buf, "TTO savegame");
199
AddDebugText(buf, "TTD savegame");
204
AddDebugText(buf, "TTDP savegame, %s format",
205
lc->oldver.type == SGT_TTDP1 ? "old" : "new");
206
if (lc->oldver.version != 0) {
207
AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
208
GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
209
GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
216
AddDebugText(buf, "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval);
220
AddDebugText(buf, "Added NewGRF: ");
221
PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum);
225
AddDebugText(buf, "Removed NewGRF: %08X", BSWAP32(lc->grfrem.grfid));
226
PrintGrfFilename(buf, lc->grfrem.grfid);
230
AddDebugText(buf, "Compatible NewGRF loaded: ");
231
PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum);
235
AddDebugText(buf, "GRF parameter changed: %08X", BSWAP32(lc->grfparam.grfid));
236
PrintGrfFilename(buf, lc->grfparam.grfid);
240
AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
241
BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
242
PrintGrfFilename(buf, lc->grfmove.grfid);
246
switch (lc->grfbug.bug) {
247
default: NOT_REACHED();
248
case GBUG_VEH_LENGTH:
249
AddDebugText(buf, "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
250
PrintGrfFilename(buf, lc->grfbug.grfid);
262
proc("---- gamelog end ----");
266
static void GamelogPrintConsoleProc(const char *s)
268
IConsolePrint(CC_WARNING, s);
271
void GamelogPrintConsole()
273
GamelogPrint(&GamelogPrintConsoleProc);
276
static int _gamelog_print_level = 0; ///< gamelog debug level we need to print stuff
278
static void GamelogPrintDebugProc(const char *s)
280
DEBUG(gamelog, _gamelog_print_level, s);
284
/** Prints gamelog to debug output. Code is executed even when
285
* there will be no output. It is called very seldom, so it
286
* doesn't matter that much. At least it gives more uniform code...
287
* @param level debug level we need to print stuff
289
void GamelogPrintDebug(int level)
291
_gamelog_print_level = level;
292
GamelogPrint(&GamelogPrintDebugProc);
296
/** Allocates new LoggedChange and new LoggedAction if needed.
297
* If there is no action active, NULL is returned.
298
* @param ct type of change
299
* @return new LoggedChange, or NULL if there is no action active
301
static LoggedChange *GamelogChange(GamelogChangeType ct)
303
if (_current_action == NULL) {
304
if (_gamelog_action_type == GLAT_NONE) return NULL;
306
_gamelog_action = ReallocT(_gamelog_action, _gamelog_actions + 1);
307
_current_action = &_gamelog_action[_gamelog_actions++];
309
_current_action->at = _gamelog_action_type;
310
_current_action->tick = _tick_counter;
311
_current_action->change = NULL;
312
_current_action->changes = 0;
315
_current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
317
LoggedChange *lc = &_current_action->change[_current_action->changes++];
324
/** Logs a emergency savegame
326
void GamelogEmergency()
328
assert(_gamelog_action_type == GLAT_EMERGENCY);
329
GamelogChange(GLCT_EMERGENCY);
332
/** Finds out if current game is a loaded emergency savegame.
334
bool GamelogTestEmergency()
336
const LoggedChange *emergency = NULL;
338
const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
339
for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
340
const LoggedChange *lcend = &la->change[la->changes];
341
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
342
if (lc->ct == GLCT_EMERGENCY) emergency = lc;
346
return (emergency != NULL);
349
/** Logs a change in game revision
350
* @param revision new revision string
352
void GamelogRevision()
354
assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
356
LoggedChange *lc = GamelogChange(GLCT_REVISION);
357
if (lc == NULL) return;
359
memset(lc->revision.text, 0, sizeof(lc->revision.text));
360
strecpy(lc->revision.text, _openttd_revision, lastof(lc->revision.text));
361
lc->revision.slver = SAVEGAME_VERSION;
362
lc->revision.modified = _openttd_revision_modified;
363
lc->revision.newgrf = _openttd_newgrf_version;
366
/** Logs a change in game mode (scenario editor or game)
370
assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
372
LoggedChange *lc = GamelogChange(GLCT_MODE);
373
if (lc == NULL) return;
375
lc->mode.mode = _game_mode;
376
lc->mode.landscape = _settings_game.game_creation.landscape;
379
/** Logs loading from savegame without gamelog
383
assert(_gamelog_action_type == GLAT_LOAD);
385
LoggedChange *lc = GamelogChange(GLCT_OLDVER);
386
if (lc == NULL) return;
388
lc->oldver.type = _savegame_type;
389
lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
392
/** Logs change in game settings. Only non-networksafe settings are logged
393
* @param name setting name
394
* @param oldval old setting value
395
* @param newval new setting value
397
void GamelogSetting(const char *name, int32 oldval, int32 newval)
399
assert(_gamelog_action_type == GLAT_SETTING);
401
LoggedChange *lc = GamelogChange(GLCT_SETTING);
402
if (lc == NULL) return;
404
lc->setting.name = strdup(name);
405
lc->setting.oldval = oldval;
406
lc->setting.newval = newval;
410
/** Finds out if current revision is different than last revision stored in the savegame.
411
* Appends GLCT_REVISION when the revision string changed
413
void GamelogTestRevision()
415
const LoggedChange *rev = NULL;
417
const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
418
for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
419
const LoggedChange *lcend = &la->change[la->changes];
420
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
421
if (lc->ct == GLCT_REVISION) rev = lc;
425
if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
426
rev->revision.modified != _openttd_revision_modified ||
427
rev->revision.newgrf != _openttd_newgrf_version) {
432
/** Finds last stored game mode or landscape.
433
* Any change is logged
435
void GamelogTestMode()
437
const LoggedChange *mode = NULL;
439
const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
440
for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
441
const LoggedChange *lcend = &la->change[la->changes];
442
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
443
if (lc->ct == GLCT_MODE) mode = lc;
447
if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
451
/** Logs triggered GRF bug.
452
* @param grfid ID of problematic GRF
453
* @param bug type of bug, @see enum GRFBugs
454
* @param data additional data
456
static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
458
assert(_gamelog_action_type == GLAT_GRFBUG);
460
LoggedChange *lc = GamelogChange(GLCT_GRFBUG);
461
if (lc == NULL) return;
463
lc->grfbug.data = data;
464
lc->grfbug.grfid = grfid;
465
lc->grfbug.bug = bug;
468
/** Logs GRF bug - rail vehicle has different length after reversing.
469
* Ensures this is logged only once for each GRF and engine type
470
* This check takes some time, but it is called pretty seldom, so it
471
* doesn't matter that much (ideally it shouldn't be called at all).
472
* @param engine engine to log
473
* @return true iff a unique record was done
475
bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
477
const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
478
for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
479
const LoggedChange *lcend = &la->change[la->changes];
480
for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
481
if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
482
lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
488
GamelogStartAction(GLAT_GRFBUG);
489
GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
496
/** Decides if GRF should be logged
497
* @param g grf to determine
498
* @return true iff GRF is not static and is loaded
500
static inline bool IsLoggableGrfConfig(const GRFConfig *g)
502
return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
505
/** Logs removal of a GRF
506
* @param grfid ID of removed GRF
508
void GamelogGRFRemove(uint32 grfid)
510
assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
512
LoggedChange *lc = GamelogChange(GLCT_GRFREM);
513
if (lc == NULL) return;
515
lc->grfrem.grfid = grfid;
518
/** Logs adding of a GRF
519
* @param newg added GRF
521
void GamelogGRFAdd(const GRFConfig *newg)
523
assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
525
if (!IsLoggableGrfConfig(newg)) return;
527
LoggedChange *lc = GamelogChange(GLCT_GRFADD);
528
if (lc == NULL) return;
530
memcpy(&lc->grfadd, newg, sizeof(GRFIdentifier));
533
/** Logs loading compatible GRF
534
* (the same ID, but different MD5 hash)
535
* @param newg new (updated) GRF
537
void GamelogGRFCompatible(const GRFIdentifier *newg)
539
assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
541
LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
542
if (lc == NULL) return;
544
memcpy(&lc->grfcompat, newg, sizeof(GRFIdentifier));
547
/** Logs changing GRF order
548
* @param grfid GRF that is moved
549
* @param offset how far it is moved, positive = moved down
551
static void GamelogGRFMove(uint32 grfid, int32 offset)
553
assert(_gamelog_action_type == GLAT_GRF);
555
LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
556
if (lc == NULL) return;
558
lc->grfmove.grfid = grfid;
559
lc->grfmove.offset = offset;
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
assert(_gamelog_action_type == GLAT_GRF);
570
LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
571
if (lc == NULL) return;
573
lc->grfparam.grfid = grfid;
576
/** Logs adding of list of GRFs.
577
* Useful when old savegame is loaded or when new game is started
578
* @param newg head of GRF linked list
580
void GamelogGRFAddList(const GRFConfig *newg)
582
assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
584
for (; newg != NULL; newg = newg->next) {
589
/** List of GRFs using array of pointers instead of linked list */
592
const GRFConfig *grf[VARARRAY_SIZE];
595
/** Generates GRFList
596
* @param grfc head of GRF linked list
598
static GRFList *GenerateGRFList(const GRFConfig *grfc)
601
for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
602
if (IsLoggableGrfConfig(g)) n++;
605
GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
608
for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
609
if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
615
/** Compares two NewGRF lists and logs any change
616
* @param oldc original GRF list
617
* @param newc new GRF list
619
void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
621
GRFList *ol = GenerateGRFList(oldc);
622
GRFList *nl = GenerateGRFList(newc);
626
while (o < ol->n && n < nl->n) {
627
const GRFConfig *og = ol->grf[o];
628
const GRFConfig *ng = nl->grf[n];
630
if (og->grfid != ng->grfid) {
632
for (oi = 0; oi < ol->n; oi++) {
633
if (ol->grf[oi]->grfid == nl->grf[n]->grfid) break;
636
/* GRF was moved, this change has been logged already */
641
/* GRF couldn't be found in the OLD list, GRF was ADDED */
642
GamelogGRFAdd(nl->grf[n++]);
645
for (ni = 0; ni < nl->n; ni++) {
646
if (nl->grf[ni]->grfid == ol->grf[o]->grfid) break;
649
/* GRF was moved, this change has been logged already */
654
/* GRF couldn't be found in the NEW list, GRF was REMOVED */
655
GamelogGRFRemove(ol->grf[o++]->grfid);
661
assert(ni > n && ni < nl->n);
662
assert(oi > o && oi < ol->n);
664
ni -= n; // number of GRFs it was moved downwards
665
oi -= o; // number of GRFs it was moved upwards
667
if (ni >= oi) { // prefer the one that is moved further
668
/* GRF was moved down */
669
GamelogGRFMove(ol->grf[o++]->grfid, ni);
671
GamelogGRFMove(nl->grf[n++]->grfid, -(int)oi);
674
if (memcmp(og->md5sum, ng->md5sum, sizeof(og->md5sum)) != 0) {
675
/* md5sum changed, probably loading 'compatible' GRF */
676
GamelogGRFCompatible(nl->grf[n]);
679
if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
680
GamelogGRFParameters(ol->grf[o]->grfid);
688
while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->grfid); // remaining GRFs were removed ...
689
while (n < nl->n) GamelogGRFAdd (nl->grf[n++]); // ... or added
696
* Get the MD5 checksum of the original NewGRF that was loaded.
697
* @param grfid the GRF ID to search for
698
* @param md5sum the MD5 checksum to write to.
700
void GamelogGetOriginalGRFMD5Checksum(uint32 grfid, byte *md5sum)
702
const LoggedAction *la = &_gamelog_action[_gamelog_actions - 1];
703
/* There should always be a "start game" action */
704
assert(_gamelog_actions > 0);
707
const LoggedChange *lc = &la->change[la->changes - 1];
708
/* There should always be at least one change per action */
709
assert(la->changes > 0);
712
if (lc->ct == GLCT_GRFADD && lc->grfadd.grfid == grfid) {
713
memcpy(md5sum, lc->grfadd.md5sum, sizeof(lc->grfadd.md5sum));
716
} while (lc-- != la->change);
717
} while (la-- != _gamelog_action);