gamelog.cpp

Go to the documentation of this file.
00001 /* $Id: gamelog.cpp 14828 2009-01-04 15:32:25Z smatz $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "saveload/saveload.h"
00008 #include "core/alloc_func.hpp"
00009 #include "core/bitmath_func.hpp"
00010 #include "core/math_func.hpp"
00011 #include "network/core/config.h"
00012 #include "variables.h"
00013 #include "string_func.h"
00014 #include "settings_type.h"
00015 #include "newgrf_config.h"
00016 #include "gamelog.h"
00017 #include "gamelog_internal.h"
00018 #include "console_func.h"
00019 #include "debug.h"
00020 #include "rev.h"
00021 
00022 #include <string.h>
00023 #include <stdarg.h>
00024 
00025 extern const uint16 SAVEGAME_VERSION;  
00026 
00027 extern SavegameType _savegame_type; 
00028 
00029 extern uint32 _ttdp_version;     
00030 extern uint16 _sl_version;       
00031 extern byte   _sl_minor_version; 
00032 
00033 
00034 static GamelogActionType _gamelog_action_type = GLAT_NONE; 
00035 
00036 LoggedAction *_gamelog_action = NULL;        
00037 uint _gamelog_actions         = 0;           
00038 static LoggedAction *_current_action = NULL; 
00039 
00040 
00045 void GamelogStartAction(GamelogActionType at)
00046 {
00047   assert(_gamelog_action_type == GLAT_NONE); // do not allow starting new action without stopping the previous first
00048   _gamelog_action_type = at;
00049 }
00050 
00053 void GamelogStopAction()
00054 {
00055   assert(_gamelog_action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress
00056 
00057   bool print = _current_action != NULL;
00058 
00059   _current_action = NULL;
00060   _gamelog_action_type = GLAT_NONE;
00061 
00062   if (print) GamelogPrintDebug(5);
00063 }
00064 
00067 void GamelogReset()
00068 {
00069   assert(_gamelog_action_type == GLAT_NONE);
00070 
00071   for (uint i = 0; i < _gamelog_actions; i++) {
00072     const LoggedAction *la = &_gamelog_action[i];
00073     for (uint j = 0; j < la->changes; j++) {
00074       const LoggedChange *lc = &la->change[j];
00075       if (lc->ct == GLCT_PATCH) free(lc->patch.name);
00076     }
00077     free(la->change);
00078   }
00079 
00080   free(_gamelog_action);
00081 
00082   _gamelog_action  = NULL;
00083   _gamelog_actions = 0;
00084   _current_action  = NULL;
00085 }
00086 
00087 enum {
00088   GAMELOG_BUF_LEN = 1024 
00089 };
00090 
00091 static int _dbgofs = 0; 
00092 
00093 static void AddDebugText(char *buf, const char *s, ...)
00094 {
00095   if (GAMELOG_BUF_LEN <= _dbgofs) return;
00096 
00097   va_list va;
00098 
00099   va_start(va, s);
00100   _dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
00101   va_end(va);
00102 }
00103 
00104 
00108 static void PrintGrfFilename(char *buf, uint grfid)
00109 {
00110   const GRFConfig *gc = FindGRFConfig(grfid);
00111 
00112   if (gc == NULL) return;
00113 
00114   AddDebugText(buf, ", filename: %s", gc->filename);
00115 }
00116 
00121 static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum)
00122 {
00123   char txt[40];
00124 
00125   md5sumToString(txt, lastof(txt), md5sum);
00126 
00127   AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
00128 
00129   PrintGrfFilename(buf, grfid);
00130 
00131   return;
00132 }
00133 
00134 
00136 static const char *la_text[] = {
00137   "new game started",
00138   "game loaded",
00139   "GRF config changed",
00140   "cheat was used",
00141   "patch settings changed",
00142   "GRF bug triggered",
00143 };
00144 
00145 assert_compile(lengthof(la_text) == GLAT_END);
00146 
00147 
00149 void GamelogPrint(GamelogPrintProc *proc)
00150 {
00151   char buf[GAMELOG_BUF_LEN];
00152 
00153   proc("---- gamelog start ----");
00154 
00155   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00156 
00157   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00158     assert((uint)la->at < GLAT_END);
00159 
00160     snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
00161     proc(buf);
00162 
00163     const LoggedChange *lcend = &la->change[la->changes];
00164 
00165     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00166       _dbgofs = 0;
00167       AddDebugText(buf, "     ");
00168 
00169       switch (lc->ct) {
00170         default: NOT_REACHED();
00171         case GLCT_MODE:
00172           AddDebugText(buf, "New game mode: %u landscape: %u",
00173             (uint)lc->mode.mode, (uint)lc->mode.landscape);
00174           break;
00175 
00176         case GLCT_REVISION:
00177           AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
00178             lc->revision.text, lc->revision.slver);
00179 
00180           switch (lc->revision.modified) {
00181             case 0: AddDebugText(buf, "not "); break;
00182             case 1: AddDebugText(buf, "maybe "); break;
00183             default: break;
00184           }
00185 
00186           AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
00187           break;
00188 
00189         case GLCT_OLDVER:
00190           AddDebugText(buf, "Conversion from ");
00191           switch (lc->oldver.type) {
00192             default: NOT_REACHED();
00193             case SGT_OTTD:
00194               AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
00195                 GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
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_PATCH:
00216           AddDebugText(buf, "Patch setting changed: %s : %d -> %d", lc->patch.name, lc->patch.oldval, lc->patch.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 
00255       proc(buf);
00256     }
00257   }
00258 
00259   proc("---- gamelog end ----");
00260 }
00261 
00262 
00263 static void GamelogPrintConsoleProc(const char *s)
00264 {
00265   IConsolePrint(CC_WARNING, s);
00266 }
00267 
00268 void GamelogPrintConsole()
00269 {
00270   GamelogPrint(&GamelogPrintConsoleProc);
00271 }
00272 
00273 static int _gamelog_print_level = 0; 
00274 
00275 static void GamelogPrintDebugProc(const char *s)
00276 {
00277   DEBUG(gamelog, _gamelog_print_level, s);
00278 }
00279 
00280 
00286 void GamelogPrintDebug(int level)
00287 {
00288   _gamelog_print_level = level;
00289   GamelogPrint(&GamelogPrintDebugProc);
00290 }
00291 
00292 
00298 static LoggedChange *GamelogChange(GamelogChangeType ct)
00299 {
00300   if (_current_action == NULL) {
00301     if (_gamelog_action_type == GLAT_NONE) return NULL;
00302 
00303     _gamelog_action  = ReallocT(_gamelog_action, _gamelog_actions + 1);
00304     _current_action  = &_gamelog_action[_gamelog_actions++];
00305 
00306     _current_action->at      = _gamelog_action_type;
00307     _current_action->tick    = _tick_counter;
00308     _current_action->change  = NULL;
00309     _current_action->changes = 0;
00310   }
00311 
00312   _current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
00313 
00314   LoggedChange *lc = &_current_action->change[_current_action->changes++];
00315   lc->ct = ct;
00316 
00317   return lc;
00318 }
00319 
00320 
00324 void GamelogRevision()
00325 {
00326   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00327 
00328   LoggedChange *lc = GamelogChange(GLCT_REVISION);
00329   if (lc == NULL) return;
00330 
00331   strecpy(lc->revision.text, _openttd_revision, lastof(lc->revision.text));
00332   lc->revision.slver = SAVEGAME_VERSION;
00333   lc->revision.modified = _openttd_revision_modified;
00334   lc->revision.newgrf = _openttd_newgrf_version;
00335 }
00336 
00339 void GamelogMode()
00340 {
00341   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
00342 
00343   LoggedChange *lc = GamelogChange(GLCT_MODE);
00344   if (lc == NULL) return;
00345 
00346   lc->mode.mode      = _game_mode;
00347   lc->mode.landscape = _settings_game.game_creation.landscape;
00348 }
00349 
00352 void GamelogOldver()
00353 {
00354   assert(_gamelog_action_type == GLAT_LOAD);
00355 
00356   LoggedChange *lc = GamelogChange(GLCT_OLDVER);
00357   if (lc == NULL) return;
00358 
00359   lc->oldver.type = _savegame_type;
00360   lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
00361 }
00362 
00368 void GamelogPatch(const char *name, int32 oldval, int32 newval)
00369 {
00370   assert(_gamelog_action_type == GLAT_PATCH);
00371 
00372   LoggedChange *lc = GamelogChange(GLCT_PATCH);
00373   if (lc == NULL) return;
00374 
00375   lc->patch.name = strdup(name);
00376   lc->patch.oldval = oldval;
00377   lc->patch.newval = newval;
00378 }
00379 
00380 
00384 void GamelogTestRevision()
00385 {
00386   const LoggedChange *rev = NULL;
00387 
00388   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00389   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00390     const LoggedChange *lcend = &la->change[la->changes];
00391     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00392       if (lc->ct == GLCT_REVISION) rev = lc;
00393     }
00394   }
00395 
00396   if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
00397       rev->revision.modified != _openttd_revision_modified ||
00398       rev->revision.newgrf != _openttd_newgrf_version) {
00399     GamelogRevision();
00400   }
00401 }
00402 
00406 void GamelogTestMode()
00407 {
00408   const LoggedChange *mode = NULL;
00409 
00410   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00411   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00412     const LoggedChange *lcend = &la->change[la->changes];
00413     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00414       if (lc->ct == GLCT_MODE) mode = lc;
00415     }
00416   }
00417 
00418   if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
00419 }
00420 
00421 
00427 static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
00428 {
00429   assert(_gamelog_action_type == GLAT_GRFBUG);
00430 
00431   LoggedChange *lc = GamelogChange(GLCT_GRFBUG);
00432   if (lc == NULL) return;
00433 
00434   lc->grfbug.data  = data;
00435   lc->grfbug.grfid = grfid;
00436   lc->grfbug.bug   = bug;
00437 }
00438 
00446 bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
00447 {
00448   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00449   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00450     const LoggedChange *lcend = &la->change[la->changes];
00451     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00452       if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
00453           lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
00454         return false;
00455       }
00456     }
00457   }
00458 
00459   GamelogStartAction(GLAT_GRFBUG);
00460   GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
00461   GamelogStopAction();
00462 
00463   return true;
00464 }
00465 
00466 
00471 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
00472 {
00473   return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
00474 }
00475 
00479 void GamelogGRFRemove(uint32 grfid)
00480 {
00481   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00482 
00483   LoggedChange *lc = GamelogChange(GLCT_GRFREM);
00484   if (lc == NULL) return;
00485 
00486   lc->grfrem.grfid = grfid;
00487 }
00488 
00492 void GamelogGRFAdd(const GRFConfig *newg)
00493 {
00494   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
00495 
00496   if (!IsLoggableGrfConfig(newg)) return;
00497 
00498   LoggedChange *lc = GamelogChange(GLCT_GRFADD);
00499   if (lc == NULL) return;
00500 
00501   memcpy(&lc->grfadd, newg, sizeof(GRFIdentifier));
00502 }
00503 
00508 void GamelogGRFCompatible(const GRFIdentifier *newg)
00509 {
00510   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00511 
00512   LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
00513   if (lc == NULL) return;
00514 
00515   memcpy(&lc->grfcompat, newg, sizeof(GRFIdentifier));
00516 }
00517 
00522 static void GamelogGRFMove(uint32 grfid, int32 offset)
00523 {
00524   assert(_gamelog_action_type == GLAT_GRF);
00525 
00526   LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
00527   if (lc == NULL) return;
00528 
00529   lc->grfmove.grfid  = grfid;
00530   lc->grfmove.offset = offset;
00531 }
00532 
00537 static void GamelogGRFParameters(uint32 grfid)
00538 {
00539   assert(_gamelog_action_type == GLAT_GRF);
00540 
00541   LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
00542   if (lc == NULL) return;
00543 
00544   lc->grfparam.grfid = grfid;
00545 }
00546 
00551 void GamelogGRFAddList(const GRFConfig *newg)
00552 {
00553   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00554 
00555   for (; newg != NULL; newg = newg->next) {
00556     GamelogGRFAdd(newg);
00557   }
00558 }
00559 
00561 struct GRFList {
00562   uint n;
00563   const GRFConfig *grf[VARARRAY_SIZE];
00564 };
00565 
00569 static GRFList *GenerateGRFList(const GRFConfig *grfc)
00570 {
00571   uint n = 0;
00572   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00573     if (IsLoggableGrfConfig(g)) n++;
00574   }
00575 
00576   GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
00577 
00578   list->n = 0;
00579   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00580     if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
00581   }
00582 
00583   return list;
00584 }
00585 
00590 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
00591 {
00592   GRFList *ol = GenerateGRFList(oldc);
00593   GRFList *nl = GenerateGRFList(newc);
00594 
00595   uint o = 0, n = 0;
00596 
00597   while (o < ol->n && n < nl->n) {
00598     const GRFConfig *og = ol->grf[o];
00599     const GRFConfig *ng = nl->grf[n];
00600 
00601     if (og->grfid != ng->grfid) {
00602       uint oi, ni;
00603       for (oi = 0; oi < ol->n; oi++) {
00604         if (ol->grf[oi]->grfid == nl->grf[n]->grfid) break;
00605       }
00606       if (oi < o) {
00607         /* GRF was moved, this change has been logged already */
00608         n++;
00609         continue;
00610       }
00611       if (oi == ol->n) {
00612         /* GRF couldn't be found in the OLD list, GRF was ADDED */
00613         GamelogGRFAdd(nl->grf[n++]);
00614         continue;
00615       }
00616       for (ni = 0; ni < nl->n; ni++) {
00617         if (nl->grf[ni]->grfid == ol->grf[o]->grfid) break;
00618       }
00619       if (ni < n) {
00620         /* GRF was moved, this change has been logged already */
00621         o++;
00622         continue;
00623       }
00624       if (ni == nl->n) {
00625         /* GRF couldn't be found in the NEW list, GRF was REMOVED */
00626         GamelogGRFRemove(ol->grf[o++]->grfid);
00627         continue;
00628       }
00629 
00630       /* o < oi < ol->n
00631        * n < ni < nl->n */
00632       assert(ni > n && ni < nl->n);
00633       assert(oi > o && oi < ol->n);
00634 
00635       ni -= n; // number of GRFs it was moved downwards
00636       oi -= o; // number of GRFs it was moved upwards
00637 
00638       if (ni >= oi) { // prefer the one that is moved further
00639         /* GRF was moved down */
00640         GamelogGRFMove(ol->grf[o++]->grfid, ni);
00641       } else {
00642         GamelogGRFMove(nl->grf[n++]->grfid, -(int)oi);
00643       }
00644     } else {
00645       if (memcmp(og->md5sum, ng->md5sum, sizeof(og->md5sum)) != 0) {
00646         /* md5sum changed, probably loading 'compatible' GRF */
00647         GamelogGRFCompatible(nl->grf[n]);
00648       }
00649 
00650       if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
00651         GamelogGRFParameters(ol->grf[o]->grfid);
00652       }
00653 
00654       o++;
00655       n++;
00656     }
00657   }
00658 
00659   while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->grfid); // remaining GRFs were removed ...
00660   while (n < nl->n) GamelogGRFAdd   (nl->grf[n++]);    // ... or added
00661 
00662   free(ol);
00663   free(nl);
00664 }

Generated on Mon Jan 5 19:01:36 2009 for openttd by  doxygen 1.5.6