gamelog.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "openttd.h"
00014 #include "saveload/saveload.h"
00015 #include "core/alloc_func.hpp"
00016 #include "variables.h"
00017 #include "string_func.h"
00018 #include "settings_type.h"
00019 #include "gamelog.h"
00020 #include "gamelog_internal.h"
00021 #include "console_func.h"
00022 #include "debug.h"
00023 #include "rev.h"
00024 
00025 #include <stdarg.h>
00026 
00027 extern const uint16 SAVEGAME_VERSION;  
00028 
00029 extern SavegameType _savegame_type; 
00030 
00031 extern uint32 _ttdp_version;     
00032 extern uint16 _sl_version;       
00033 extern byte   _sl_minor_version; 
00034 
00035 
00036 static GamelogActionType _gamelog_action_type = GLAT_NONE; 
00037 
00038 LoggedAction *_gamelog_action = NULL;        
00039 uint _gamelog_actions         = 0;           
00040 static LoggedAction *_current_action = NULL; 
00041 
00042 
00047 void GamelogStartAction(GamelogActionType at)
00048 {
00049   assert(_gamelog_action_type == GLAT_NONE); // do not allow starting new action without stopping the previous first
00050   _gamelog_action_type = at;
00051 }
00052 
00055 void GamelogStopAction()
00056 {
00057   assert(_gamelog_action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress
00058 
00059   bool print = _current_action != NULL;
00060 
00061   _current_action = NULL;
00062   _gamelog_action_type = GLAT_NONE;
00063 
00064   if (print) GamelogPrintDebug(5);
00065 }
00066 
00069 void GamelogReset()
00070 {
00071   assert(_gamelog_action_type == GLAT_NONE);
00072 
00073   for (uint i = 0; i < _gamelog_actions; i++) {
00074     const LoggedAction *la = &_gamelog_action[i];
00075     for (uint j = 0; j < la->changes; j++) {
00076       const LoggedChange *lc = &la->change[j];
00077       if (lc->ct == GLCT_SETTING) free(lc->setting.name);
00078     }
00079     free(la->change);
00080   }
00081 
00082   free(_gamelog_action);
00083 
00084   _gamelog_action  = NULL;
00085   _gamelog_actions = 0;
00086   _current_action  = NULL;
00087 }
00088 
00089 enum {
00090   GAMELOG_BUF_LEN = 1024 
00091 };
00092 
00093 static int _dbgofs = 0; 
00094 
00095 static void AddDebugText(char *buf, const char *s, ...) WARN_FORMAT(2, 3);
00096 
00097 static void AddDebugText(char *buf, const char *s, ...)
00098 {
00099   if (GAMELOG_BUF_LEN <= _dbgofs) return;
00100 
00101   va_list va;
00102 
00103   va_start(va, s);
00104   _dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
00105   va_end(va);
00106 }
00107 
00108 
00113 static void PrintGrfFilename(char *buf, uint grfid)
00114 {
00115   const GRFConfig *gc = FindGRFConfig(grfid);
00116 
00117   if (gc == NULL) return;
00118 
00119   AddDebugText(buf, ", filename: %s", gc->filename);
00120 }
00121 
00127 static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum)
00128 {
00129   char txt[40];
00130 
00131   md5sumToString(txt, lastof(txt), md5sum);
00132 
00133   AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
00134 
00135   PrintGrfFilename(buf, grfid);
00136 
00137   return;
00138 }
00139 
00140 
00142 static const char * const la_text[] = {
00143   "new game started",
00144   "game loaded",
00145   "GRF config changed",
00146   "cheat was used",
00147   "settings changed",
00148   "GRF bug triggered",
00149   "emergency savegame",
00150 };
00151 
00152 assert_compile(lengthof(la_text) == GLAT_END);
00153 
00154 
00159 void GamelogPrint(GamelogPrintProc *proc)
00160 {
00161   char buf[GAMELOG_BUF_LEN];
00162 
00163   proc("---- gamelog start ----");
00164 
00165   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00166 
00167   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00168     assert((uint)la->at < GLAT_END);
00169 
00170     snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
00171     proc(buf);
00172 
00173     const LoggedChange *lcend = &la->change[la->changes];
00174 
00175     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00176       _dbgofs = 0;
00177       AddDebugText(buf, "     ");
00178 
00179       switch (lc->ct) {
00180         default: NOT_REACHED();
00181         case GLCT_MODE:
00182           AddDebugText(buf, "New game mode: %u landscape: %u",
00183             (uint)lc->mode.mode, (uint)lc->mode.landscape);
00184           break;
00185 
00186         case GLCT_REVISION:
00187           AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
00188             lc->revision.text, lc->revision.slver);
00189 
00190           switch (lc->revision.modified) {
00191             case 0: AddDebugText(buf, "not "); break;
00192             case 1: AddDebugText(buf, "maybe "); break;
00193             default: break;
00194           }
00195 
00196           AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
00197           break;
00198 
00199         case GLCT_OLDVER:
00200           AddDebugText(buf, "Conversion from ");
00201           switch (lc->oldver.type) {
00202             default: NOT_REACHED();
00203             case SGT_OTTD:
00204               AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
00205                 GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
00206               break;
00207 
00208             case SGT_TTO:
00209               AddDebugText(buf, "TTO savegame");
00210               break;
00211 
00212             case SGT_TTD:
00213               AddDebugText(buf, "TTD savegame");
00214               break;
00215 
00216             case SGT_TTDP1:
00217             case SGT_TTDP2:
00218               AddDebugText(buf, "TTDP savegame, %s format",
00219                 lc->oldver.type == SGT_TTDP1 ? "old" : "new");
00220               if (lc->oldver.version != 0) {
00221                 AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
00222                   GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
00223                   GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
00224               }
00225               break;
00226           }
00227           break;
00228 
00229         case GLCT_SETTING:
00230           AddDebugText(buf, "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval);
00231           break;
00232 
00233         case GLCT_GRFADD:
00234           AddDebugText(buf, "Added NewGRF: ");
00235           PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum);
00236           break;
00237 
00238         case GLCT_GRFREM:
00239           AddDebugText(buf, "Removed NewGRF: %08X", BSWAP32(lc->grfrem.grfid));
00240           PrintGrfFilename(buf, lc->grfrem.grfid);
00241           break;
00242 
00243         case GLCT_GRFCOMPAT:
00244           AddDebugText(buf, "Compatible NewGRF loaded: ");
00245           PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum);
00246           break;
00247 
00248         case GLCT_GRFPARAM:
00249           AddDebugText(buf, "GRF parameter changed: %08X", BSWAP32(lc->grfparam.grfid));
00250           PrintGrfFilename(buf, lc->grfparam.grfid);
00251           break;
00252 
00253         case GLCT_GRFMOVE:
00254           AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
00255             BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
00256           PrintGrfFilename(buf, lc->grfmove.grfid);
00257           break;
00258 
00259         case GLCT_GRFBUG:
00260           switch (lc->grfbug.bug) {
00261             default: NOT_REACHED();
00262             case GBUG_VEH_LENGTH:
00263               AddDebugText(buf, "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
00264               PrintGrfFilename(buf, lc->grfbug.grfid);
00265               break;
00266           }
00267 
00268         case GLCT_EMERGENCY:
00269           break;
00270       }
00271 
00272       proc(buf);
00273     }
00274   }
00275 
00276   proc("---- gamelog end ----");
00277 }
00278 
00279 
00280 static void GamelogPrintConsoleProc(const char *s)
00281 {
00282   IConsolePrint(CC_WARNING, s);
00283 }
00284 
00285 void GamelogPrintConsole()
00286 {
00287   GamelogPrint(&GamelogPrintConsoleProc);
00288 }
00289 
00290 static int _gamelog_print_level = 0; 
00291 
00292 static void GamelogPrintDebugProc(const char *s)
00293 {
00294   DEBUG(gamelog, _gamelog_print_level, "%s", s);
00295 }
00296 
00297 
00303 void GamelogPrintDebug(int level)
00304 {
00305   _gamelog_print_level = level;
00306   GamelogPrint(&GamelogPrintDebugProc);
00307 }
00308 
00309 
00315 static LoggedChange *GamelogChange(GamelogChangeType ct)
00316 {
00317   if (_current_action == NULL) {
00318     if (_gamelog_action_type == GLAT_NONE) return NULL;
00319 
00320     _gamelog_action  = ReallocT(_gamelog_action, _gamelog_actions + 1);
00321     _current_action  = &_gamelog_action[_gamelog_actions++];
00322 
00323     _current_action->at      = _gamelog_action_type;
00324     _current_action->tick    = _tick_counter;
00325     _current_action->change  = NULL;
00326     _current_action->changes = 0;
00327   }
00328 
00329   _current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
00330 
00331   LoggedChange *lc = &_current_action->change[_current_action->changes++];
00332   lc->ct = ct;
00333 
00334   return lc;
00335 }
00336 
00337 
00340 void GamelogEmergency()
00341 {
00342   /* Terminate any active action */
00343   if (_gamelog_action_type != GLAT_NONE) GamelogStopAction();
00344   GamelogStartAction(GLAT_EMERGENCY);
00345   GamelogChange(GLCT_EMERGENCY);
00346   GamelogStopAction();
00347 }
00348 
00351 bool GamelogTestEmergency()
00352 {
00353   const LoggedChange *emergency = NULL;
00354 
00355   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00356   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00357     const LoggedChange *lcend = &la->change[la->changes];
00358     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00359       if (lc->ct == GLCT_EMERGENCY) emergency = lc;
00360     }
00361   }
00362 
00363   return (emergency != NULL);
00364 }
00365 
00368 void GamelogRevision()
00369 {
00370   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00371 
00372   LoggedChange *lc = GamelogChange(GLCT_REVISION);
00373   if (lc == NULL) return;
00374 
00375   memset(lc->revision.text, 0, sizeof(lc->revision.text));
00376   strecpy(lc->revision.text, _openttd_revision, lastof(lc->revision.text));
00377   lc->revision.slver = SAVEGAME_VERSION;
00378   lc->revision.modified = _openttd_revision_modified;
00379   lc->revision.newgrf = _openttd_newgrf_version;
00380 }
00381 
00384 void GamelogMode()
00385 {
00386   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
00387 
00388   LoggedChange *lc = GamelogChange(GLCT_MODE);
00389   if (lc == NULL) return;
00390 
00391   lc->mode.mode      = _game_mode;
00392   lc->mode.landscape = _settings_game.game_creation.landscape;
00393 }
00394 
00397 void GamelogOldver()
00398 {
00399   assert(_gamelog_action_type == GLAT_LOAD);
00400 
00401   LoggedChange *lc = GamelogChange(GLCT_OLDVER);
00402   if (lc == NULL) return;
00403 
00404   lc->oldver.type = _savegame_type;
00405   lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
00406 }
00407 
00413 void GamelogSetting(const char *name, int32 oldval, int32 newval)
00414 {
00415   assert(_gamelog_action_type == GLAT_SETTING);
00416 
00417   LoggedChange *lc = GamelogChange(GLCT_SETTING);
00418   if (lc == NULL) return;
00419 
00420   lc->setting.name = strdup(name);
00421   lc->setting.oldval = oldval;
00422   lc->setting.newval = newval;
00423 }
00424 
00425 
00429 void GamelogTestRevision()
00430 {
00431   const LoggedChange *rev = NULL;
00432 
00433   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00434   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00435     const LoggedChange *lcend = &la->change[la->changes];
00436     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00437       if (lc->ct == GLCT_REVISION) rev = lc;
00438     }
00439   }
00440 
00441   if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
00442       rev->revision.modified != _openttd_revision_modified ||
00443       rev->revision.newgrf != _openttd_newgrf_version) {
00444     GamelogRevision();
00445   }
00446 }
00447 
00451 void GamelogTestMode()
00452 {
00453   const LoggedChange *mode = NULL;
00454 
00455   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00456   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00457     const LoggedChange *lcend = &la->change[la->changes];
00458     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00459       if (lc->ct == GLCT_MODE) mode = lc;
00460     }
00461   }
00462 
00463   if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
00464 }
00465 
00466 
00472 static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
00473 {
00474   assert(_gamelog_action_type == GLAT_GRFBUG);
00475 
00476   LoggedChange *lc = GamelogChange(GLCT_GRFBUG);
00477   if (lc == NULL) return;
00478 
00479   lc->grfbug.data  = data;
00480   lc->grfbug.grfid = grfid;
00481   lc->grfbug.bug   = bug;
00482 }
00483 
00492 bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
00493 {
00494   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00495   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00496     const LoggedChange *lcend = &la->change[la->changes];
00497     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00498       if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
00499           lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
00500         return false;
00501       }
00502     }
00503   }
00504 
00505   GamelogStartAction(GLAT_GRFBUG);
00506   GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
00507   GamelogStopAction();
00508 
00509   return true;
00510 }
00511 
00512 
00517 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
00518 {
00519   return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
00520 }
00521 
00525 void GamelogGRFRemove(uint32 grfid)
00526 {
00527   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00528 
00529   LoggedChange *lc = GamelogChange(GLCT_GRFREM);
00530   if (lc == NULL) return;
00531 
00532   lc->grfrem.grfid = grfid;
00533 }
00534 
00538 void GamelogGRFAdd(const GRFConfig *newg)
00539 {
00540   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
00541 
00542   if (!IsLoggableGrfConfig(newg)) return;
00543 
00544   LoggedChange *lc = GamelogChange(GLCT_GRFADD);
00545   if (lc == NULL) return;
00546 
00547   memcpy(&lc->grfadd, newg, sizeof(GRFIdentifier));
00548 }
00549 
00554 void GamelogGRFCompatible(const GRFIdentifier *newg)
00555 {
00556   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00557 
00558   LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
00559   if (lc == NULL) return;
00560 
00561   memcpy(&lc->grfcompat, newg, sizeof(GRFIdentifier));
00562 }
00563 
00568 static void GamelogGRFMove(uint32 grfid, int32 offset)
00569 {
00570   assert(_gamelog_action_type == GLAT_GRF);
00571 
00572   LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
00573   if (lc == NULL) return;
00574 
00575   lc->grfmove.grfid  = grfid;
00576   lc->grfmove.offset = offset;
00577 }
00578 
00583 static void GamelogGRFParameters(uint32 grfid)
00584 {
00585   assert(_gamelog_action_type == GLAT_GRF);
00586 
00587   LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
00588   if (lc == NULL) return;
00589 
00590   lc->grfparam.grfid = grfid;
00591 }
00592 
00597 void GamelogGRFAddList(const GRFConfig *newg)
00598 {
00599   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00600 
00601   for (; newg != NULL; newg = newg->next) {
00602     GamelogGRFAdd(newg);
00603   }
00604 }
00605 
00607 struct GRFList {
00608   uint n;
00609   const GRFConfig *grf[];
00610 };
00611 
00615 static GRFList *GenerateGRFList(const GRFConfig *grfc)
00616 {
00617   uint n = 0;
00618   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00619     if (IsLoggableGrfConfig(g)) n++;
00620   }
00621 
00622   GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
00623 
00624   list->n = 0;
00625   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00626     if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
00627   }
00628 
00629   return list;
00630 }
00631 
00636 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
00637 {
00638   GRFList *ol = GenerateGRFList(oldc);
00639   GRFList *nl = GenerateGRFList(newc);
00640 
00641   uint o = 0, n = 0;
00642 
00643   while (o < ol->n && n < nl->n) {
00644     const GRFConfig *og = ol->grf[o];
00645     const GRFConfig *ng = nl->grf[n];
00646 
00647     if (og->grfid != ng->grfid) {
00648       uint oi, ni;
00649       for (oi = 0; oi < ol->n; oi++) {
00650         if (ol->grf[oi]->grfid == nl->grf[n]->grfid) break;
00651       }
00652       if (oi < o) {
00653         /* GRF was moved, this change has been logged already */
00654         n++;
00655         continue;
00656       }
00657       if (oi == ol->n) {
00658         /* GRF couldn't be found in the OLD list, GRF was ADDED */
00659         GamelogGRFAdd(nl->grf[n++]);
00660         continue;
00661       }
00662       for (ni = 0; ni < nl->n; ni++) {
00663         if (nl->grf[ni]->grfid == ol->grf[o]->grfid) break;
00664       }
00665       if (ni < n) {
00666         /* GRF was moved, this change has been logged already */
00667         o++;
00668         continue;
00669       }
00670       if (ni == nl->n) {
00671         /* GRF couldn't be found in the NEW list, GRF was REMOVED */
00672         GamelogGRFRemove(ol->grf[o++]->grfid);
00673         continue;
00674       }
00675 
00676       /* o < oi < ol->n
00677        * n < ni < nl->n */
00678       assert(ni > n && ni < nl->n);
00679       assert(oi > o && oi < ol->n);
00680 
00681       ni -= n; // number of GRFs it was moved downwards
00682       oi -= o; // number of GRFs it was moved upwards
00683 
00684       if (ni >= oi) { // prefer the one that is moved further
00685         /* GRF was moved down */
00686         GamelogGRFMove(ol->grf[o++]->grfid, ni);
00687       } else {
00688         GamelogGRFMove(nl->grf[n++]->grfid, -(int)oi);
00689       }
00690     } else {
00691       if (memcmp(og->md5sum, ng->md5sum, sizeof(og->md5sum)) != 0) {
00692         /* md5sum changed, probably loading 'compatible' GRF */
00693         GamelogGRFCompatible(nl->grf[n]);
00694       }
00695 
00696       if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
00697         GamelogGRFParameters(ol->grf[o]->grfid);
00698       }
00699 
00700       o++;
00701       n++;
00702     }
00703   }
00704 
00705   while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->grfid); // remaining GRFs were removed ...
00706   while (n < nl->n) GamelogGRFAdd   (nl->grf[n++]);    // ... or added
00707 
00708   free(ol);
00709   free(nl);
00710 }
00711 
00717 void GamelogGetOriginalGRFMD5Checksum(uint32 grfid, byte *md5sum)
00718 {
00719   const LoggedAction *la = &_gamelog_action[_gamelog_actions - 1];
00720   /* There should always be a "start game" action */
00721   assert(_gamelog_actions > 0);
00722 
00723   do {
00724     const LoggedChange *lc = &la->change[la->changes - 1];
00725     /* There should always be at least one change per action */
00726     assert(la->changes > 0);
00727 
00728     do {
00729       if (lc->ct == GLCT_GRFADD && lc->grfadd.grfid == grfid) {
00730         memcpy(md5sum, lc->grfadd.md5sum, sizeof(lc->grfadd.md5sum));
00731         return;
00732       }
00733     } while (lc-- != la->change);
00734   } while (la-- != _gamelog_action);
00735 
00736   NOT_REACHED();
00737 }

Generated on Sat Dec 26 20:06:00 2009 for OpenTTD by  doxygen 1.5.6