00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "saveload/saveload.h"
00008 #include "core/alloc_func.hpp"
00009 #include "variables.h"
00010 #include "string_func.h"
00011 #include "settings_type.h"
00012 #include "gamelog.h"
00013 #include "gamelog_internal.h"
00014 #include "console_func.h"
00015 #include "debug.h"
00016 #include "rev.h"
00017
00018 #include <stdarg.h>
00019
00020 extern const uint16 SAVEGAME_VERSION;
00021
00022 extern SavegameType _savegame_type;
00023
00024 extern uint32 _ttdp_version;
00025 extern uint16 _sl_version;
00026 extern byte _sl_minor_version;
00027
00028
00029 static GamelogActionType _gamelog_action_type = GLAT_NONE;
00030
00031 LoggedAction *_gamelog_action = NULL;
00032 uint _gamelog_actions = 0;
00033 static LoggedAction *_current_action = NULL;
00034
00035
00040 void GamelogStartAction(GamelogActionType at)
00041 {
00042 assert(_gamelog_action_type == GLAT_NONE);
00043 _gamelog_action_type = at;
00044 }
00045
00048 void GamelogStopAction()
00049 {
00050 assert(_gamelog_action_type != GLAT_NONE);
00051
00052 bool print = _current_action != NULL;
00053
00054 _current_action = NULL;
00055 _gamelog_action_type = GLAT_NONE;
00056
00057 if (print) GamelogPrintDebug(5);
00058 }
00059
00062 void GamelogReset()
00063 {
00064 assert(_gamelog_action_type == GLAT_NONE);
00065
00066 for (uint i = 0; i < _gamelog_actions; i++) {
00067 const LoggedAction *la = &_gamelog_action[i];
00068 for (uint j = 0; j < la->changes; j++) {
00069 const LoggedChange *lc = &la->change[j];
00070 if (lc->ct == GLCT_SETTING) free(lc->setting.name);
00071 }
00072 free(la->change);
00073 }
00074
00075 free(_gamelog_action);
00076
00077 _gamelog_action = NULL;
00078 _gamelog_actions = 0;
00079 _current_action = NULL;
00080 }
00081
00082 enum {
00083 GAMELOG_BUF_LEN = 1024
00084 };
00085
00086 static int _dbgofs = 0;
00087
00088 static void AddDebugText(char *buf, const char *s, ...)
00089 {
00090 if (GAMELOG_BUF_LEN <= _dbgofs) return;
00091
00092 va_list va;
00093
00094 va_start(va, s);
00095 _dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
00096 va_end(va);
00097 }
00098
00099
00103 static void PrintGrfFilename(char *buf, uint grfid)
00104 {
00105 const GRFConfig *gc = FindGRFConfig(grfid);
00106
00107 if (gc == NULL) return;
00108
00109 AddDebugText(buf, ", filename: %s", gc->filename);
00110 }
00111
00116 static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum)
00117 {
00118 char txt[40];
00119
00120 md5sumToString(txt, lastof(txt), md5sum);
00121
00122 AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
00123
00124 PrintGrfFilename(buf, grfid);
00125
00126 return;
00127 }
00128
00129
00131 static const char *la_text[] = {
00132 "new game started",
00133 "game loaded",
00134 "GRF config changed",
00135 "cheat was used",
00136 "settings changed",
00137 "GRF bug triggered",
00138 "emergency savegame",
00139 };
00140
00141 assert_compile(lengthof(la_text) == GLAT_END);
00142
00143
00145 void GamelogPrint(GamelogPrintProc *proc)
00146 {
00147 char buf[GAMELOG_BUF_LEN];
00148
00149 proc("---- gamelog start ----");
00150
00151 const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00152
00153 for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00154 assert((uint)la->at < GLAT_END);
00155
00156 snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
00157 proc(buf);
00158
00159 const LoggedChange *lcend = &la->change[la->changes];
00160
00161 for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00162 _dbgofs = 0;
00163 AddDebugText(buf, " ");
00164
00165 switch (lc->ct) {
00166 default: NOT_REACHED();
00167 case GLCT_MODE:
00168 AddDebugText(buf, "New game mode: %u landscape: %u",
00169 (uint)lc->mode.mode, (uint)lc->mode.landscape);
00170 break;
00171
00172 case GLCT_REVISION:
00173 AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
00174 lc->revision.text, lc->revision.slver);
00175
00176 switch (lc->revision.modified) {
00177 case 0: AddDebugText(buf, "not "); break;
00178 case 1: AddDebugText(buf, "maybe "); break;
00179 default: break;
00180 }
00181
00182 AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
00183 break;
00184
00185 case GLCT_OLDVER:
00186 AddDebugText(buf, "Conversion from ");
00187 switch (lc->oldver.type) {
00188 default: NOT_REACHED();
00189 case SGT_OTTD:
00190 AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
00191 GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
00192 break;
00193
00194 case SGT_TTO:
00195 AddDebugText(buf, "TTO savegame");
00196 break;
00197
00198 case SGT_TTD:
00199 AddDebugText(buf, "TTD savegame");
00200 break;
00201
00202 case SGT_TTDP1:
00203 case SGT_TTDP2:
00204 AddDebugText(buf, "TTDP savegame, %s format",
00205 lc->oldver.type == SGT_TTDP1 ? "old" : "new");
00206 if (lc->oldver.version != 0) {
00207 AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
00208 GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
00209 GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
00210 }
00211 break;
00212 }
00213 break;
00214
00215 case GLCT_SETTING:
00216 AddDebugText(buf, "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval);
00217 break;
00218
00219 case GLCT_GRFADD:
00220 AddDebugText(buf, "Added NewGRF: ");
00221 PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum);
00222 break;
00223
00224 case GLCT_GRFREM:
00225 AddDebugText(buf, "Removed NewGRF: %08X", BSWAP32(lc->grfrem.grfid));
00226 PrintGrfFilename(buf, lc->grfrem.grfid);
00227 break;
00228
00229 case GLCT_GRFCOMPAT:
00230 AddDebugText(buf, "Compatible NewGRF loaded: ");
00231 PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum);
00232 break;
00233
00234 case GLCT_GRFPARAM:
00235 AddDebugText(buf, "GRF parameter changed: %08X", BSWAP32(lc->grfparam.grfid));
00236 PrintGrfFilename(buf, lc->grfparam.grfid);
00237 break;
00238
00239 case GLCT_GRFMOVE:
00240 AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
00241 BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
00242 PrintGrfFilename(buf, lc->grfmove.grfid);
00243 break;
00244
00245 case GLCT_GRFBUG:
00246 switch (lc->grfbug.bug) {
00247 default: NOT_REACHED();
00248 case GBUG_VEH_LENGTH:
00249 AddDebugText(buf, "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
00250 PrintGrfFilename(buf, lc->grfbug.grfid);
00251 break;
00252 }
00253
00254 case GLCT_EMERGENCY:
00255 break;
00256 }
00257
00258 proc(buf);
00259 }
00260 }
00261
00262 proc("---- gamelog end ----");
00263 }
00264
00265
00266 static void GamelogPrintConsoleProc(const char *s)
00267 {
00268 IConsolePrint(CC_WARNING, s);
00269 }
00270
00271 void GamelogPrintConsole()
00272 {
00273 GamelogPrint(&GamelogPrintConsoleProc);
00274 }
00275
00276 static int _gamelog_print_level = 0;
00277
00278 static void GamelogPrintDebugProc(const char *s)
00279 {
00280 DEBUG(gamelog, _gamelog_print_level, s);
00281 }
00282
00283
00289 void GamelogPrintDebug(int level)
00290 {
00291 _gamelog_print_level = level;
00292 GamelogPrint(&GamelogPrintDebugProc);
00293 }
00294
00295
00301 static LoggedChange *GamelogChange(GamelogChangeType ct)
00302 {
00303 if (_current_action == NULL) {
00304 if (_gamelog_action_type == GLAT_NONE) return NULL;
00305
00306 _gamelog_action = ReallocT(_gamelog_action, _gamelog_actions + 1);
00307 _current_action = &_gamelog_action[_gamelog_actions++];
00308
00309 _current_action->at = _gamelog_action_type;
00310 _current_action->tick = _tick_counter;
00311 _current_action->change = NULL;
00312 _current_action->changes = 0;
00313 }
00314
00315 _current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
00316
00317 LoggedChange *lc = &_current_action->change[_current_action->changes++];
00318 lc->ct = ct;
00319
00320 return lc;
00321 }
00322
00323
00326 void GamelogEmergency()
00327 {
00328 assert(_gamelog_action_type == GLAT_EMERGENCY);
00329 GamelogChange(GLCT_EMERGENCY);
00330 }
00331
00334 bool GamelogTestEmergency()
00335 {
00336 const LoggedChange *emergency = NULL;
00337
00338 const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00339 for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00340 const LoggedChange *lcend = &la->change[la->changes];
00341 for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00342 if (lc->ct == GLCT_EMERGENCY) emergency = lc;
00343 }
00344 }
00345
00346 return (emergency != NULL);
00347 }
00348
00352 void GamelogRevision()
00353 {
00354 assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00355
00356 LoggedChange *lc = GamelogChange(GLCT_REVISION);
00357 if (lc == NULL) return;
00358
00359 memset(lc->revision.text, 0, sizeof(lc->revision.text));
00360 strecpy(lc->revision.text, _openttd_revision, lastof(lc->revision.text));
00361 lc->revision.slver = SAVEGAME_VERSION;
00362 lc->revision.modified = _openttd_revision_modified;
00363 lc->revision.newgrf = _openttd_newgrf_version;
00364 }
00365
00368 void GamelogMode()
00369 {
00370 assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
00371
00372 LoggedChange *lc = GamelogChange(GLCT_MODE);
00373 if (lc == NULL) return;
00374
00375 lc->mode.mode = _game_mode;
00376 lc->mode.landscape = _settings_game.game_creation.landscape;
00377 }
00378
00381 void GamelogOldver()
00382 {
00383 assert(_gamelog_action_type == GLAT_LOAD);
00384
00385 LoggedChange *lc = GamelogChange(GLCT_OLDVER);
00386 if (lc == NULL) return;
00387
00388 lc->oldver.type = _savegame_type;
00389 lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
00390 }
00391
00397 void GamelogSetting(const char *name, int32 oldval, int32 newval)
00398 {
00399 assert(_gamelog_action_type == GLAT_SETTING);
00400
00401 LoggedChange *lc = GamelogChange(GLCT_SETTING);
00402 if (lc == NULL) return;
00403
00404 lc->setting.name = strdup(name);
00405 lc->setting.oldval = oldval;
00406 lc->setting.newval = newval;
00407 }
00408
00409
00413 void GamelogTestRevision()
00414 {
00415 const LoggedChange *rev = NULL;
00416
00417 const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00418 for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00419 const LoggedChange *lcend = &la->change[la->changes];
00420 for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00421 if (lc->ct == GLCT_REVISION) rev = lc;
00422 }
00423 }
00424
00425 if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
00426 rev->revision.modified != _openttd_revision_modified ||
00427 rev->revision.newgrf != _openttd_newgrf_version) {
00428 GamelogRevision();
00429 }
00430 }
00431
00435 void GamelogTestMode()
00436 {
00437 const LoggedChange *mode = NULL;
00438
00439 const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00440 for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00441 const LoggedChange *lcend = &la->change[la->changes];
00442 for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00443 if (lc->ct == GLCT_MODE) mode = lc;
00444 }
00445 }
00446
00447 if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
00448 }
00449
00450
00456 static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
00457 {
00458 assert(_gamelog_action_type == GLAT_GRFBUG);
00459
00460 LoggedChange *lc = GamelogChange(GLCT_GRFBUG);
00461 if (lc == NULL) return;
00462
00463 lc->grfbug.data = data;
00464 lc->grfbug.grfid = grfid;
00465 lc->grfbug.bug = bug;
00466 }
00467
00475 bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
00476 {
00477 const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00478 for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00479 const LoggedChange *lcend = &la->change[la->changes];
00480 for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00481 if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
00482 lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
00483 return false;
00484 }
00485 }
00486 }
00487
00488 GamelogStartAction(GLAT_GRFBUG);
00489 GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
00490 GamelogStopAction();
00491
00492 return true;
00493 }
00494
00495
00500 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
00501 {
00502 return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
00503 }
00504
00508 void GamelogGRFRemove(uint32 grfid)
00509 {
00510 assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00511
00512 LoggedChange *lc = GamelogChange(GLCT_GRFREM);
00513 if (lc == NULL) return;
00514
00515 lc->grfrem.grfid = grfid;
00516 }
00517
00521 void GamelogGRFAdd(const GRFConfig *newg)
00522 {
00523 assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
00524
00525 if (!IsLoggableGrfConfig(newg)) return;
00526
00527 LoggedChange *lc = GamelogChange(GLCT_GRFADD);
00528 if (lc == NULL) return;
00529
00530 memcpy(&lc->grfadd, newg, sizeof(GRFIdentifier));
00531 }
00532
00537 void GamelogGRFCompatible(const GRFIdentifier *newg)
00538 {
00539 assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00540
00541 LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
00542 if (lc == NULL) return;
00543
00544 memcpy(&lc->grfcompat, newg, sizeof(GRFIdentifier));
00545 }
00546
00551 static void GamelogGRFMove(uint32 grfid, int32 offset)
00552 {
00553 assert(_gamelog_action_type == GLAT_GRF);
00554
00555 LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
00556 if (lc == NULL) return;
00557
00558 lc->grfmove.grfid = grfid;
00559 lc->grfmove.offset = offset;
00560 }
00561
00566 static void GamelogGRFParameters(uint32 grfid)
00567 {
00568 assert(_gamelog_action_type == GLAT_GRF);
00569
00570 LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
00571 if (lc == NULL) return;
00572
00573 lc->grfparam.grfid = grfid;
00574 }
00575
00580 void GamelogGRFAddList(const GRFConfig *newg)
00581 {
00582 assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00583
00584 for (; newg != NULL; newg = newg->next) {
00585 GamelogGRFAdd(newg);
00586 }
00587 }
00588
00590 struct GRFList {
00591 uint n;
00592 const GRFConfig *grf[VARARRAY_SIZE];
00593 };
00594
00598 static GRFList *GenerateGRFList(const GRFConfig *grfc)
00599 {
00600 uint n = 0;
00601 for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00602 if (IsLoggableGrfConfig(g)) n++;
00603 }
00604
00605 GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
00606
00607 list->n = 0;
00608 for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00609 if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
00610 }
00611
00612 return list;
00613 }
00614
00619 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
00620 {
00621 GRFList *ol = GenerateGRFList(oldc);
00622 GRFList *nl = GenerateGRFList(newc);
00623
00624 uint o = 0, n = 0;
00625
00626 while (o < ol->n && n < nl->n) {
00627 const GRFConfig *og = ol->grf[o];
00628 const GRFConfig *ng = nl->grf[n];
00629
00630 if (og->grfid != ng->grfid) {
00631 uint oi, ni;
00632 for (oi = 0; oi < ol->n; oi++) {
00633 if (ol->grf[oi]->grfid == nl->grf[n]->grfid) break;
00634 }
00635 if (oi < o) {
00636
00637 n++;
00638 continue;
00639 }
00640 if (oi == ol->n) {
00641
00642 GamelogGRFAdd(nl->grf[n++]);
00643 continue;
00644 }
00645 for (ni = 0; ni < nl->n; ni++) {
00646 if (nl->grf[ni]->grfid == ol->grf[o]->grfid) break;
00647 }
00648 if (ni < n) {
00649
00650 o++;
00651 continue;
00652 }
00653 if (ni == nl->n) {
00654
00655 GamelogGRFRemove(ol->grf[o++]->grfid);
00656 continue;
00657 }
00658
00659
00660
00661 assert(ni > n && ni < nl->n);
00662 assert(oi > o && oi < ol->n);
00663
00664 ni -= n;
00665 oi -= o;
00666
00667 if (ni >= oi) {
00668
00669 GamelogGRFMove(ol->grf[o++]->grfid, ni);
00670 } else {
00671 GamelogGRFMove(nl->grf[n++]->grfid, -(int)oi);
00672 }
00673 } else {
00674 if (memcmp(og->md5sum, ng->md5sum, sizeof(og->md5sum)) != 0) {
00675
00676 GamelogGRFCompatible(nl->grf[n]);
00677 }
00678
00679 if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
00680 GamelogGRFParameters(ol->grf[o]->grfid);
00681 }
00682
00683 o++;
00684 n++;
00685 }
00686 }
00687
00688 while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->grfid);
00689 while (n < nl->n) GamelogGRFAdd (nl->grf[n++]);
00690
00691 free(ol);
00692 free(nl);
00693 }
00694
00700 void GamelogGetOriginalGRFMD5Checksum(uint32 grfid, byte *md5sum)
00701 {
00702 const LoggedAction *la = &_gamelog_action[_gamelog_actions - 1];
00703
00704 assert(_gamelog_actions > 0);
00705
00706 do {
00707 const LoggedChange *lc = &la->change[la->changes - 1];
00708
00709 assert(la->changes > 0);
00710
00711 do {
00712 if (lc->ct == GLCT_GRFADD && lc->grfadd.grfid == grfid) {
00713 memcpy(md5sum, lc->grfadd.md5sum, sizeof(lc->grfadd.md5sum));
00714 return;
00715 }
00716 } while (lc-- != la->change);
00717 } while (la-- != _gamelog_action);
00718
00719 NOT_REACHED();
00720 }