gamelog.cpp

Go to the documentation of this file.
00001 /* $Id: gamelog.cpp 16273 2009-05-10 21:33:55Z 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, ...) WARN_FORMAT(2, 3);
00089 
00090 static void AddDebugText(char *buf, const char *s, ...)
00091 {
00092   if (GAMELOG_BUF_LEN <= _dbgofs) return;
00093 
00094   va_list va;
00095 
00096   va_start(va, s);
00097   _dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
00098   va_end(va);
00099 }
00100 
00101 
00105 static void PrintGrfFilename(char *buf, uint grfid)
00106 {
00107   const GRFConfig *gc = FindGRFConfig(grfid);
00108 
00109   if (gc == NULL) return;
00110 
00111   AddDebugText(buf, ", filename: %s", gc->filename);
00112 }
00113 
00118 static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum)
00119 {
00120   char txt[40];
00121 
00122   md5sumToString(txt, lastof(txt), md5sum);
00123 
00124   AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
00125 
00126   PrintGrfFilename(buf, grfid);
00127 
00128   return;
00129 }
00130 
00131 
00133 static const char *la_text[] = {
00134   "new game started",
00135   "game loaded",
00136   "GRF config changed",
00137   "cheat was used",
00138   "settings changed",
00139   "GRF bug triggered",
00140   "emergency savegame",
00141 };
00142 
00143 assert_compile(lengthof(la_text) == GLAT_END);
00144 
00145 
00147 void GamelogPrint(GamelogPrintProc *proc)
00148 {
00149   char buf[GAMELOG_BUF_LEN];
00150 
00151   proc("---- gamelog start ----");
00152 
00153   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00154 
00155   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00156     assert((uint)la->at < GLAT_END);
00157 
00158     snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
00159     proc(buf);
00160 
00161     const LoggedChange *lcend = &la->change[la->changes];
00162 
00163     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00164       _dbgofs = 0;
00165       AddDebugText(buf, "     ");
00166 
00167       switch (lc->ct) {
00168         default: NOT_REACHED();
00169         case GLCT_MODE:
00170           AddDebugText(buf, "New game mode: %u landscape: %u",
00171             (uint)lc->mode.mode, (uint)lc->mode.landscape);
00172           break;
00173 
00174         case GLCT_REVISION:
00175           AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
00176             lc->revision.text, lc->revision.slver);
00177 
00178           switch (lc->revision.modified) {
00179             case 0: AddDebugText(buf, "not "); break;
00180             case 1: AddDebugText(buf, "maybe "); break;
00181             default: break;
00182           }
00183 
00184           AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
00185           break;
00186 
00187         case GLCT_OLDVER:
00188           AddDebugText(buf, "Conversion from ");
00189           switch (lc->oldver.type) {
00190             default: NOT_REACHED();
00191             case SGT_OTTD:
00192               AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
00193                 GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
00194               break;
00195 
00196             case SGT_TTO:
00197               AddDebugText(buf, "TTO savegame");
00198               break;
00199 
00200             case SGT_TTD:
00201               AddDebugText(buf, "TTD savegame");
00202               break;
00203 
00204             case SGT_TTDP1:
00205             case SGT_TTDP2:
00206               AddDebugText(buf, "TTDP savegame, %s format",
00207                 lc->oldver.type == SGT_TTDP1 ? "old" : "new");
00208               if (lc->oldver.version != 0) {
00209                 AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
00210                   GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
00211                   GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
00212               }
00213               break;
00214           }
00215           break;
00216 
00217         case GLCT_SETTING:
00218           AddDebugText(buf, "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval);
00219           break;
00220 
00221         case GLCT_GRFADD:
00222           AddDebugText(buf, "Added NewGRF: ");
00223           PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum);
00224           break;
00225 
00226         case GLCT_GRFREM:
00227           AddDebugText(buf, "Removed NewGRF: %08X", BSWAP32(lc->grfrem.grfid));
00228           PrintGrfFilename(buf, lc->grfrem.grfid);
00229           break;
00230 
00231         case GLCT_GRFCOMPAT:
00232           AddDebugText(buf, "Compatible NewGRF loaded: ");
00233           PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum);
00234           break;
00235 
00236         case GLCT_GRFPARAM:
00237           AddDebugText(buf, "GRF parameter changed: %08X", BSWAP32(lc->grfparam.grfid));
00238           PrintGrfFilename(buf, lc->grfparam.grfid);
00239           break;
00240 
00241         case GLCT_GRFMOVE:
00242           AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
00243             BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
00244           PrintGrfFilename(buf, lc->grfmove.grfid);
00245           break;
00246 
00247         case GLCT_GRFBUG:
00248           switch (lc->grfbug.bug) {
00249             default: NOT_REACHED();
00250             case GBUG_VEH_LENGTH:
00251               AddDebugText(buf, "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
00252               PrintGrfFilename(buf, lc->grfbug.grfid);
00253               break;
00254           }
00255 
00256         case GLCT_EMERGENCY:
00257           break;
00258       }
00259 
00260       proc(buf);
00261     }
00262   }
00263 
00264   proc("---- gamelog end ----");
00265 }
00266 
00267 
00268 static void GamelogPrintConsoleProc(const char *s)
00269 {
00270   IConsolePrint(CC_WARNING, s);
00271 }
00272 
00273 void GamelogPrintConsole()
00274 {
00275   GamelogPrint(&GamelogPrintConsoleProc);
00276 }
00277 
00278 static int _gamelog_print_level = 0; 
00279 
00280 static void GamelogPrintDebugProc(const char *s)
00281 {
00282   DEBUG(gamelog, _gamelog_print_level, "%s", s);
00283 }
00284 
00285 
00291 void GamelogPrintDebug(int level)
00292 {
00293   _gamelog_print_level = level;
00294   GamelogPrint(&GamelogPrintDebugProc);
00295 }
00296 
00297 
00303 static LoggedChange *GamelogChange(GamelogChangeType ct)
00304 {
00305   if (_current_action == NULL) {
00306     if (_gamelog_action_type == GLAT_NONE) return NULL;
00307 
00308     _gamelog_action  = ReallocT(_gamelog_action, _gamelog_actions + 1);
00309     _current_action  = &_gamelog_action[_gamelog_actions++];
00310 
00311     _current_action->at      = _gamelog_action_type;
00312     _current_action->tick    = _tick_counter;
00313     _current_action->change  = NULL;
00314     _current_action->changes = 0;
00315   }
00316 
00317   _current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
00318 
00319   LoggedChange *lc = &_current_action->change[_current_action->changes++];
00320   lc->ct = ct;
00321 
00322   return lc;
00323 }
00324 
00325 
00328 void GamelogEmergency()
00329 {
00330   assert(_gamelog_action_type == GLAT_EMERGENCY);
00331   GamelogChange(GLCT_EMERGENCY);
00332 }
00333 
00336 bool GamelogTestEmergency()
00337 {
00338   const LoggedChange *emergency = NULL;
00339 
00340   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00341   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00342     const LoggedChange *lcend = &la->change[la->changes];
00343     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00344       if (lc->ct == GLCT_EMERGENCY) emergency = lc;
00345     }
00346   }
00347 
00348   return (emergency != NULL);
00349 }
00350 
00354 void GamelogRevision()
00355 {
00356   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00357 
00358   LoggedChange *lc = GamelogChange(GLCT_REVISION);
00359   if (lc == NULL) return;
00360 
00361   memset(lc->revision.text, 0, sizeof(lc->revision.text));
00362   strecpy(lc->revision.text, _openttd_revision, lastof(lc->revision.text));
00363   lc->revision.slver = SAVEGAME_VERSION;
00364   lc->revision.modified = _openttd_revision_modified;
00365   lc->revision.newgrf = _openttd_newgrf_version;
00366 }
00367 
00370 void GamelogMode()
00371 {
00372   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
00373 
00374   LoggedChange *lc = GamelogChange(GLCT_MODE);
00375   if (lc == NULL) return;
00376 
00377   lc->mode.mode      = _game_mode;
00378   lc->mode.landscape = _settings_game.game_creation.landscape;
00379 }
00380 
00383 void GamelogOldver()
00384 {
00385   assert(_gamelog_action_type == GLAT_LOAD);
00386 
00387   LoggedChange *lc = GamelogChange(GLCT_OLDVER);
00388   if (lc == NULL) return;
00389 
00390   lc->oldver.type = _savegame_type;
00391   lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
00392 }
00393 
00399 void GamelogSetting(const char *name, int32 oldval, int32 newval)
00400 {
00401   assert(_gamelog_action_type == GLAT_SETTING);
00402 
00403   LoggedChange *lc = GamelogChange(GLCT_SETTING);
00404   if (lc == NULL) return;
00405 
00406   lc->setting.name = strdup(name);
00407   lc->setting.oldval = oldval;
00408   lc->setting.newval = newval;
00409 }
00410 
00411 
00415 void GamelogTestRevision()
00416 {
00417   const LoggedChange *rev = NULL;
00418 
00419   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00420   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00421     const LoggedChange *lcend = &la->change[la->changes];
00422     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00423       if (lc->ct == GLCT_REVISION) rev = lc;
00424     }
00425   }
00426 
00427   if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
00428       rev->revision.modified != _openttd_revision_modified ||
00429       rev->revision.newgrf != _openttd_newgrf_version) {
00430     GamelogRevision();
00431   }
00432 }
00433 
00437 void GamelogTestMode()
00438 {
00439   const LoggedChange *mode = NULL;
00440 
00441   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00442   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00443     const LoggedChange *lcend = &la->change[la->changes];
00444     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00445       if (lc->ct == GLCT_MODE) mode = lc;
00446     }
00447   }
00448 
00449   if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
00450 }
00451 
00452 
00458 static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
00459 {
00460   assert(_gamelog_action_type == GLAT_GRFBUG);
00461 
00462   LoggedChange *lc = GamelogChange(GLCT_GRFBUG);
00463   if (lc == NULL) return;
00464 
00465   lc->grfbug.data  = data;
00466   lc->grfbug.grfid = grfid;
00467   lc->grfbug.bug   = bug;
00468 }
00469 
00477 bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
00478 {
00479   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00480   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00481     const LoggedChange *lcend = &la->change[la->changes];
00482     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00483       if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
00484           lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
00485         return false;
00486       }
00487     }
00488   }
00489 
00490   GamelogStartAction(GLAT_GRFBUG);
00491   GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
00492   GamelogStopAction();
00493 
00494   return true;
00495 }
00496 
00497 
00502 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
00503 {
00504   return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
00505 }
00506 
00510 void GamelogGRFRemove(uint32 grfid)
00511 {
00512   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00513 
00514   LoggedChange *lc = GamelogChange(GLCT_GRFREM);
00515   if (lc == NULL) return;
00516 
00517   lc->grfrem.grfid = grfid;
00518 }
00519 
00523 void GamelogGRFAdd(const GRFConfig *newg)
00524 {
00525   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
00526 
00527   if (!IsLoggableGrfConfig(newg)) return;
00528 
00529   LoggedChange *lc = GamelogChange(GLCT_GRFADD);
00530   if (lc == NULL) return;
00531 
00532   memcpy(&lc->grfadd, newg, sizeof(GRFIdentifier));
00533 }
00534 
00539 void GamelogGRFCompatible(const GRFIdentifier *newg)
00540 {
00541   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00542 
00543   LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
00544   if (lc == NULL) return;
00545 
00546   memcpy(&lc->grfcompat, newg, sizeof(GRFIdentifier));
00547 }
00548 
00553 static void GamelogGRFMove(uint32 grfid, int32 offset)
00554 {
00555   assert(_gamelog_action_type == GLAT_GRF);
00556 
00557   LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
00558   if (lc == NULL) return;
00559 
00560   lc->grfmove.grfid  = grfid;
00561   lc->grfmove.offset = offset;
00562 }
00563 
00568 static void GamelogGRFParameters(uint32 grfid)
00569 {
00570   assert(_gamelog_action_type == GLAT_GRF);
00571 
00572   LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
00573   if (lc == NULL) return;
00574 
00575   lc->grfparam.grfid = grfid;
00576 }
00577 
00582 void GamelogGRFAddList(const GRFConfig *newg)
00583 {
00584   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00585 
00586   for (; newg != NULL; newg = newg->next) {
00587     GamelogGRFAdd(newg);
00588   }
00589 }
00590 
00592 struct GRFList {
00593   uint n;
00594   const GRFConfig *grf[VARARRAY_SIZE];
00595 };
00596 
00600 static GRFList *GenerateGRFList(const GRFConfig *grfc)
00601 {
00602   uint n = 0;
00603   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00604     if (IsLoggableGrfConfig(g)) n++;
00605   }
00606 
00607   GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
00608 
00609   list->n = 0;
00610   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00611     if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
00612   }
00613 
00614   return list;
00615 }
00616 
00621 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
00622 {
00623   GRFList *ol = GenerateGRFList(oldc);
00624   GRFList *nl = GenerateGRFList(newc);
00625 
00626   uint o = 0, n = 0;
00627 
00628   while (o < ol->n && n < nl->n) {
00629     const GRFConfig *og = ol->grf[o];
00630     const GRFConfig *ng = nl->grf[n];
00631 
00632     if (og->grfid != ng->grfid) {
00633       uint oi, ni;
00634       for (oi = 0; oi < ol->n; oi++) {
00635         if (ol->grf[oi]->grfid == nl->grf[n]->grfid) break;
00636       }
00637       if (oi < o) {
00638         /* GRF was moved, this change has been logged already */
00639         n++;
00640         continue;
00641       }
00642       if (oi == ol->n) {
00643         /* GRF couldn't be found in the OLD list, GRF was ADDED */
00644         GamelogGRFAdd(nl->grf[n++]);
00645         continue;
00646       }
00647       for (ni = 0; ni < nl->n; ni++) {
00648         if (nl->grf[ni]->grfid == ol->grf[o]->grfid) break;
00649       }
00650       if (ni < n) {
00651         /* GRF was moved, this change has been logged already */
00652         o++;
00653         continue;
00654       }
00655       if (ni == nl->n) {
00656         /* GRF couldn't be found in the NEW list, GRF was REMOVED */
00657         GamelogGRFRemove(ol->grf[o++]->grfid);
00658         continue;
00659       }
00660 
00661       /* o < oi < ol->n
00662        * n < ni < nl->n */
00663       assert(ni > n && ni < nl->n);
00664       assert(oi > o && oi < ol->n);
00665 
00666       ni -= n; // number of GRFs it was moved downwards
00667       oi -= o; // number of GRFs it was moved upwards
00668 
00669       if (ni >= oi) { // prefer the one that is moved further
00670         /* GRF was moved down */
00671         GamelogGRFMove(ol->grf[o++]->grfid, ni);
00672       } else {
00673         GamelogGRFMove(nl->grf[n++]->grfid, -(int)oi);
00674       }
00675     } else {
00676       if (memcmp(og->md5sum, ng->md5sum, sizeof(og->md5sum)) != 0) {
00677         /* md5sum changed, probably loading 'compatible' GRF */
00678         GamelogGRFCompatible(nl->grf[n]);
00679       }
00680 
00681       if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
00682         GamelogGRFParameters(ol->grf[o]->grfid);
00683       }
00684 
00685       o++;
00686       n++;
00687     }
00688   }
00689 
00690   while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->grfid); // remaining GRFs were removed ...
00691   while (n < nl->n) GamelogGRFAdd   (nl->grf[n++]);    // ... or added
00692 
00693   free(ol);
00694   free(nl);
00695 }
00696 
00702 void GamelogGetOriginalGRFMD5Checksum(uint32 grfid, byte *md5sum)
00703 {
00704   const LoggedAction *la = &_gamelog_action[_gamelog_actions - 1];
00705   /* There should always be a "start game" action */
00706   assert(_gamelog_actions > 0);
00707 
00708   do {
00709     const LoggedChange *lc = &la->change[la->changes - 1];
00710     /* There should always be at least one change per action */
00711     assert(la->changes > 0);
00712 
00713     do {
00714       if (lc->ct == GLCT_GRFADD && lc->grfadd.grfid == grfid) {
00715         memcpy(md5sum, lc->grfadd.md5sum, sizeof(lc->grfadd.md5sum));
00716         return;
00717       }
00718     } while (lc-- != la->change);
00719   } while (la-- != _gamelog_action);
00720 
00721   NOT_REACHED();
00722 }

Generated on Wed Dec 23 20:12:48 2009 for OpenTTD by  doxygen 1.5.6