gamelog.cpp

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

Generated on Fri Apr 30 21:55:20 2010 for OpenTTD by  doxygen 1.6.1