gamelog.cpp

Go to the documentation of this file.
00001 /* $Id: gamelog.cpp 15904 2009-03-30 23:22:13Z rubidium $ */
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); // do not allow starting new action without stopping the previous first
00043   _gamelog_action_type = at;
00044 }
00045 
00048 void GamelogStopAction()
00049 {
00050   assert(_gamelog_action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress
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         /* GRF was moved, this change has been logged already */
00637         n++;
00638         continue;
00639       }
00640       if (oi == ol->n) {
00641         /* GRF couldn't be found in the OLD list, GRF was ADDED */
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         /* GRF was moved, this change has been logged already */
00650         o++;
00651         continue;
00652       }
00653       if (ni == nl->n) {
00654         /* GRF couldn't be found in the NEW list, GRF was REMOVED */
00655         GamelogGRFRemove(ol->grf[o++]->grfid);
00656         continue;
00657       }
00658 
00659       /* o < oi < ol->n
00660        * n < ni < nl->n */
00661       assert(ni > n && ni < nl->n);
00662       assert(oi > o && oi < ol->n);
00663 
00664       ni -= n; // number of GRFs it was moved downwards
00665       oi -= o; // number of GRFs it was moved upwards
00666 
00667       if (ni >= oi) { // prefer the one that is moved further
00668         /* GRF was moved down */
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         /* md5sum changed, probably loading 'compatible' GRF */
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); // remaining GRFs were removed ...
00689   while (n < nl->n) GamelogGRFAdd   (nl->grf[n++]);    // ... or added
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   /* There should always be a "start game" action */
00704   assert(_gamelog_actions > 0);
00705 
00706   do {
00707     const LoggedChange *lc = &la->change[la->changes - 1];
00708     /* There should always be at least one change per action */
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 }

Generated on Wed Apr 1 14:38:06 2009 for OpenTTD by  doxygen 1.5.6