gamelog.cpp

Go to the documentation of this file.
00001 /* $Id: gamelog.cpp 23901 2012-02-05 15:51:13Z smatz $ */
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 "saveload/saveload.h"
00014 #include "string_func.h"
00015 #include "settings_type.h"
00016 #include "gamelog_internal.h"
00017 #include "console_func.h"
00018 #include "debug.h"
00019 #include "date_func.h"
00020 #include "rev.h"
00021 
00022 #include <stdarg.h>
00023 
00024 extern const uint16 SAVEGAME_VERSION;  
00025 
00026 extern SavegameType _savegame_type; 
00027 
00028 extern uint32 _ttdp_version;     
00029 extern uint16 _sl_version;       
00030 extern byte   _sl_minor_version; 
00031 
00032 
00033 static GamelogActionType _gamelog_action_type = GLAT_NONE; 
00034 
00035 LoggedAction *_gamelog_action = NULL;        
00036 uint _gamelog_actions         = 0;           
00037 static LoggedAction *_current_action = NULL; 
00038 
00039 
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 
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 
00069 void GamelogFree(LoggedAction *gamelog_action, uint gamelog_actions)
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_SETTING) free(lc->setting.name);
00076     }
00077     free(la->change);
00078   }
00079 
00080   free(gamelog_action);
00081 }
00082 
00086 void GamelogReset()
00087 {
00088   assert(_gamelog_action_type == GLAT_NONE);
00089   GamelogFree(_gamelog_action, _gamelog_actions);
00090 
00091   _gamelog_action  = NULL;
00092   _gamelog_actions = 0;
00093   _current_action  = NULL;
00094 }
00095 
00096 static const uint GAMELOG_BUF_LEN = 1024; 
00097 
00098 static uint _dbgofs = 0; 
00099 
00100 static void AddDebugText(char *buf, const char *s, ...) WARN_FORMAT(2, 3);
00101 
00102 static void AddDebugText(char *buf, const char *s, ...)
00103 {
00104   if (GAMELOG_BUF_LEN <= _dbgofs) return;
00105 
00106   va_list va;
00107 
00108   va_start(va, s);
00109   _dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
00110   va_end(va);
00111 }
00112 
00113 
00121 static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum, const GRFConfig *gc)
00122 {
00123   char txt[40];
00124 
00125   if (md5sum != NULL) {
00126     md5sumToString(txt, lastof(txt), md5sum);
00127     AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
00128   } else {
00129     AddDebugText(buf, "GRF ID %08X", BSWAP32(grfid));
00130   }
00131 
00132   if (gc != NULL) {
00133     AddDebugText(buf, ", filename: %s (md5sum matches)", gc->filename);
00134   } else {
00135     gc = FindGRFConfig(grfid, FGCM_ANY);
00136     if (gc != NULL) {
00137       AddDebugText(buf, ", filename: %s (matches GRFID only)", gc->filename);
00138     } else {
00139       AddDebugText(buf, ", unknown GRF");
00140     }
00141   }
00142   return;
00143 }
00144 
00145 
00147 static const char * const la_text[] = {
00148   "new game started",
00149   "game loaded",
00150   "GRF config changed",
00151   "cheat was used",
00152   "settings changed",
00153   "GRF bug triggered",
00154   "emergency savegame",
00155 };
00156 
00157 assert_compile(lengthof(la_text) == GLAT_END);
00158 
00166 struct GRFPresence{
00167   const GRFConfig *gc;  
00168   bool was_missing;     
00169 
00170   GRFPresence(const GRFConfig *gc) : gc(gc), was_missing(false) {}
00171 };
00172 typedef SmallMap<uint32, GRFPresence> GrfIDMapping;
00173 
00178 void GamelogPrint(GamelogPrintProc *proc)
00179 {
00180   char buf[GAMELOG_BUF_LEN];
00181   GrfIDMapping grf_names;
00182 
00183   proc("---- gamelog start ----");
00184 
00185   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00186 
00187   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00188     assert((uint)la->at < GLAT_END);
00189 
00190     snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
00191     proc(buf);
00192 
00193     const LoggedChange *lcend = &la->change[la->changes];
00194 
00195     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00196       _dbgofs = 0;
00197       AddDebugText(buf, "     ");
00198 
00199       switch (lc->ct) {
00200         default: NOT_REACHED();
00201         case GLCT_MODE:
00202           AddDebugText(buf, "New game mode: %u landscape: %u",
00203             (uint)lc->mode.mode, (uint)lc->mode.landscape);
00204           break;
00205 
00206         case GLCT_REVISION:
00207           AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
00208             lc->revision.text, lc->revision.slver);
00209 
00210           switch (lc->revision.modified) {
00211             case 0: AddDebugText(buf, "not "); break;
00212             case 1: AddDebugText(buf, "maybe "); break;
00213             default: break;
00214           }
00215 
00216           AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
00217           break;
00218 
00219         case GLCT_OLDVER:
00220           AddDebugText(buf, "Conversion from ");
00221           switch (lc->oldver.type) {
00222             default: NOT_REACHED();
00223             case SGT_OTTD:
00224               AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
00225                 GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
00226               break;
00227 
00228             case SGT_TTO:
00229               AddDebugText(buf, "TTO savegame");
00230               break;
00231 
00232             case SGT_TTD:
00233               AddDebugText(buf, "TTD savegame");
00234               break;
00235 
00236             case SGT_TTDP1:
00237             case SGT_TTDP2:
00238               AddDebugText(buf, "TTDP savegame, %s format",
00239                 lc->oldver.type == SGT_TTDP1 ? "old" : "new");
00240               if (lc->oldver.version != 0) {
00241                 AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
00242                   GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
00243                   GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
00244               }
00245               break;
00246           }
00247           break;
00248 
00249         case GLCT_SETTING:
00250           AddDebugText(buf, "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval);
00251           break;
00252 
00253         case GLCT_GRFADD: {
00254           const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum);
00255           AddDebugText(buf, "Added NewGRF: ");
00256           PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum, gc);
00257           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
00258           if (gm != grf_names.End() && !gm->second.was_missing) AddDebugText(buf, ". Gamelog inconsistency: GrfID was already added!");
00259           grf_names[lc->grfadd.grfid] = gc;
00260           break;
00261         }
00262 
00263         case GLCT_GRFREM: {
00264           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
00265           AddDebugText(buf, la->at == GLAT_LOAD ? "Missing NewGRF: " : "Removed NewGRF: ");
00266           PrintGrfInfo(buf, lc->grfrem.grfid, NULL, gm != grf_names.End() ? gm->second.gc : NULL);
00267           if (gm == grf_names.End()) {
00268             AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
00269           } else {
00270             if (la->at == GLAT_LOAD) {
00271               /* Missing grfs on load are not removed from the configuration */
00272               gm->second.was_missing = true;
00273             } else {
00274               grf_names.Erase(gm);
00275             }
00276           }
00277           break;
00278         }
00279 
00280         case GLCT_GRFCOMPAT: {
00281           const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum);
00282           AddDebugText(buf, "Compatible NewGRF loaded: ");
00283           PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum, gc);
00284           if (!grf_names.Contains(lc->grfcompat.grfid)) AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
00285           grf_names[lc->grfcompat.grfid] = gc;
00286           break;
00287         }
00288 
00289         case GLCT_GRFPARAM: {
00290           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
00291           AddDebugText(buf, "GRF parameter changed: ");
00292           PrintGrfInfo(buf, lc->grfparam.grfid, NULL, gm != grf_names.End() ? gm->second.gc : NULL);
00293           if (gm == grf_names.End()) AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
00294           break;
00295         }
00296 
00297         case GLCT_GRFMOVE: {
00298           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
00299           AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
00300             BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
00301           PrintGrfInfo(buf, lc->grfmove.grfid, NULL, gm != grf_names.End() ? gm->second.gc : NULL);
00302           if (gm == grf_names.End()) AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
00303           break;
00304         }
00305 
00306         case GLCT_GRFBUG: {
00307           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
00308           switch (lc->grfbug.bug) {
00309             default: NOT_REACHED();
00310             case GBUG_VEH_LENGTH:
00311               AddDebugText(buf, "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
00312               break;
00313           }
00314           PrintGrfInfo(buf, lc->grfbug.grfid, NULL, gm != grf_names.End() ? gm->second.gc : NULL);
00315           if (gm == grf_names.End()) AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
00316           break;
00317         }
00318 
00319         case GLCT_EMERGENCY:
00320           break;
00321       }
00322 
00323       proc(buf);
00324     }
00325   }
00326 
00327   proc("---- gamelog end ----");
00328 }
00329 
00330 
00331 static void GamelogPrintConsoleProc(const char *s)
00332 {
00333   IConsolePrint(CC_WARNING, s);
00334 }
00335 
00337 void GamelogPrintConsole()
00338 {
00339   GamelogPrint(&GamelogPrintConsoleProc);
00340 }
00341 
00342 static int _gamelog_print_level = 0; 
00343 
00344 static void GamelogPrintDebugProc(const char *s)
00345 {
00346   DEBUG(gamelog, _gamelog_print_level, "%s", s);
00347 }
00348 
00349 
00356 void GamelogPrintDebug(int level)
00357 {
00358   _gamelog_print_level = level;
00359   GamelogPrint(&GamelogPrintDebugProc);
00360 }
00361 
00362 
00369 static LoggedChange *GamelogChange(GamelogChangeType ct)
00370 {
00371   if (_current_action == NULL) {
00372     if (_gamelog_action_type == GLAT_NONE) return NULL;
00373 
00374     _gamelog_action  = ReallocT(_gamelog_action, _gamelog_actions + 1);
00375     _current_action  = &_gamelog_action[_gamelog_actions++];
00376 
00377     _current_action->at      = _gamelog_action_type;
00378     _current_action->tick    = _tick_counter;
00379     _current_action->change  = NULL;
00380     _current_action->changes = 0;
00381   }
00382 
00383   _current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
00384 
00385   LoggedChange *lc = &_current_action->change[_current_action->changes++];
00386   lc->ct = ct;
00387 
00388   return lc;
00389 }
00390 
00391 
00395 void GamelogEmergency()
00396 {
00397   /* Terminate any active action */
00398   if (_gamelog_action_type != GLAT_NONE) GamelogStopAction();
00399   GamelogStartAction(GLAT_EMERGENCY);
00400   GamelogChange(GLCT_EMERGENCY);
00401   GamelogStopAction();
00402 }
00403 
00407 bool GamelogTestEmergency()
00408 {
00409   const LoggedChange *emergency = NULL;
00410 
00411   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00412   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00413     const LoggedChange *lcend = &la->change[la->changes];
00414     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00415       if (lc->ct == GLCT_EMERGENCY) emergency = lc;
00416     }
00417   }
00418 
00419   return (emergency != NULL);
00420 }
00421 
00425 void GamelogRevision()
00426 {
00427   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00428 
00429   LoggedChange *lc = GamelogChange(GLCT_REVISION);
00430   if (lc == NULL) return;
00431 
00432   memset(lc->revision.text, 0, sizeof(lc->revision.text));
00433   strecpy(lc->revision.text, _openttd_revision, lastof(lc->revision.text));
00434   lc->revision.slver = SAVEGAME_VERSION;
00435   lc->revision.modified = _openttd_revision_modified;
00436   lc->revision.newgrf = _openttd_newgrf_version;
00437 }
00438 
00442 void GamelogMode()
00443 {
00444   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
00445 
00446   LoggedChange *lc = GamelogChange(GLCT_MODE);
00447   if (lc == NULL) return;
00448 
00449   lc->mode.mode      = _game_mode;
00450   lc->mode.landscape = _settings_game.game_creation.landscape;
00451 }
00452 
00456 void GamelogOldver()
00457 {
00458   assert(_gamelog_action_type == GLAT_LOAD);
00459 
00460   LoggedChange *lc = GamelogChange(GLCT_OLDVER);
00461   if (lc == NULL) return;
00462 
00463   lc->oldver.type = _savegame_type;
00464   lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
00465 }
00466 
00473 void GamelogSetting(const char *name, int32 oldval, int32 newval)
00474 {
00475   assert(_gamelog_action_type == GLAT_SETTING);
00476 
00477   LoggedChange *lc = GamelogChange(GLCT_SETTING);
00478   if (lc == NULL) return;
00479 
00480   lc->setting.name = strdup(name);
00481   lc->setting.oldval = oldval;
00482   lc->setting.newval = newval;
00483 }
00484 
00485 
00490 void GamelogTestRevision()
00491 {
00492   const LoggedChange *rev = NULL;
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_REVISION) rev = lc;
00499     }
00500   }
00501 
00502   if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
00503       rev->revision.modified != _openttd_revision_modified ||
00504       rev->revision.newgrf != _openttd_newgrf_version) {
00505     GamelogRevision();
00506   }
00507 }
00508 
00513 void GamelogTestMode()
00514 {
00515   const LoggedChange *mode = NULL;
00516 
00517   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00518   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00519     const LoggedChange *lcend = &la->change[la->changes];
00520     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00521       if (lc->ct == GLCT_MODE) mode = lc;
00522     }
00523   }
00524 
00525   if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
00526 }
00527 
00528 
00535 static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
00536 {
00537   assert(_gamelog_action_type == GLAT_GRFBUG);
00538 
00539   LoggedChange *lc = GamelogChange(GLCT_GRFBUG);
00540   if (lc == NULL) return;
00541 
00542   lc->grfbug.data  = data;
00543   lc->grfbug.grfid = grfid;
00544   lc->grfbug.bug   = bug;
00545 }
00546 
00556 bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
00557 {
00558   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
00559   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
00560     const LoggedChange *lcend = &la->change[la->changes];
00561     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00562       if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
00563           lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
00564         return false;
00565       }
00566     }
00567   }
00568 
00569   GamelogStartAction(GLAT_GRFBUG);
00570   GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
00571   GamelogStopAction();
00572 
00573   return true;
00574 }
00575 
00576 
00582 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
00583 {
00584   return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
00585 }
00586 
00591 void GamelogGRFRemove(uint32 grfid)
00592 {
00593   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00594 
00595   LoggedChange *lc = GamelogChange(GLCT_GRFREM);
00596   if (lc == NULL) return;
00597 
00598   lc->grfrem.grfid = grfid;
00599 }
00600 
00605 void GamelogGRFAdd(const GRFConfig *newg)
00606 {
00607   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
00608 
00609   if (!IsLoggableGrfConfig(newg)) return;
00610 
00611   LoggedChange *lc = GamelogChange(GLCT_GRFADD);
00612   if (lc == NULL) return;
00613 
00614   lc->grfadd = newg->ident;
00615 }
00616 
00622 void GamelogGRFCompatible(const GRFIdentifier *newg)
00623 {
00624   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
00625 
00626   LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
00627   if (lc == NULL) return;
00628 
00629   lc->grfcompat = *newg;
00630 }
00631 
00637 static void GamelogGRFMove(uint32 grfid, int32 offset)
00638 {
00639   assert(_gamelog_action_type == GLAT_GRF);
00640 
00641   LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
00642   if (lc == NULL) return;
00643 
00644   lc->grfmove.grfid  = grfid;
00645   lc->grfmove.offset = offset;
00646 }
00647 
00653 static void GamelogGRFParameters(uint32 grfid)
00654 {
00655   assert(_gamelog_action_type == GLAT_GRF);
00656 
00657   LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
00658   if (lc == NULL) return;
00659 
00660   lc->grfparam.grfid = grfid;
00661 }
00662 
00668 void GamelogGRFAddList(const GRFConfig *newg)
00669 {
00670   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
00671 
00672   for (; newg != NULL; newg = newg->next) {
00673     GamelogGRFAdd(newg);
00674   }
00675 }
00676 
00678 struct GRFList {
00679   uint n;
00680   const GRFConfig *grf[];
00681 };
00682 
00687 static GRFList *GenerateGRFList(const GRFConfig *grfc)
00688 {
00689   uint n = 0;
00690   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00691     if (IsLoggableGrfConfig(g)) n++;
00692   }
00693 
00694   GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
00695 
00696   list->n = 0;
00697   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
00698     if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
00699   }
00700 
00701   return list;
00702 }
00703 
00709 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
00710 {
00711   GRFList *ol = GenerateGRFList(oldc);
00712   GRFList *nl = GenerateGRFList(newc);
00713 
00714   uint o = 0, n = 0;
00715 
00716   while (o < ol->n && n < nl->n) {
00717     const GRFConfig *og = ol->grf[o];
00718     const GRFConfig *ng = nl->grf[n];
00719 
00720     if (og->ident.grfid != ng->ident.grfid) {
00721       uint oi, ni;
00722       for (oi = 0; oi < ol->n; oi++) {
00723         if (ol->grf[oi]->ident.grfid == nl->grf[n]->ident.grfid) break;
00724       }
00725       if (oi < o) {
00726         /* GRF was moved, this change has been logged already */
00727         n++;
00728         continue;
00729       }
00730       if (oi == ol->n) {
00731         /* GRF couldn't be found in the OLD list, GRF was ADDED */
00732         GamelogGRFAdd(nl->grf[n++]);
00733         continue;
00734       }
00735       for (ni = 0; ni < nl->n; ni++) {
00736         if (nl->grf[ni]->ident.grfid == ol->grf[o]->ident.grfid) break;
00737       }
00738       if (ni < n) {
00739         /* GRF was moved, this change has been logged already */
00740         o++;
00741         continue;
00742       }
00743       if (ni == nl->n) {
00744         /* GRF couldn't be found in the NEW list, GRF was REMOVED */
00745         GamelogGRFRemove(ol->grf[o++]->ident.grfid);
00746         continue;
00747       }
00748 
00749       /* o < oi < ol->n
00750        * n < ni < nl->n */
00751       assert(ni > n && ni < nl->n);
00752       assert(oi > o && oi < ol->n);
00753 
00754       ni -= n; // number of GRFs it was moved downwards
00755       oi -= o; // number of GRFs it was moved upwards
00756 
00757       if (ni >= oi) { // prefer the one that is moved further
00758         /* GRF was moved down */
00759         GamelogGRFMove(ol->grf[o++]->ident.grfid, ni);
00760       } else {
00761         GamelogGRFMove(nl->grf[n++]->ident.grfid, -(int)oi);
00762       }
00763     } else {
00764       if (memcmp(og->ident.md5sum, ng->ident.md5sum, sizeof(og->ident.md5sum)) != 0) {
00765         /* md5sum changed, probably loading 'compatible' GRF */
00766         GamelogGRFCompatible(&nl->grf[n]->ident);
00767       }
00768 
00769       if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
00770         GamelogGRFParameters(ol->grf[o]->ident.grfid);
00771       }
00772 
00773       o++;
00774       n++;
00775     }
00776   }
00777 
00778   while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->ident.grfid); // remaining GRFs were removed ...
00779   while (n < nl->n) GamelogGRFAdd   (nl->grf[n++]);              // ... or added
00780 
00781   free(ol);
00782   free(nl);
00783 }
00784 
00793 void GamelogInfo(LoggedAction *gamelog_action, uint gamelog_actions, uint32 *last_ottd_rev, byte *ever_modified, bool *removed_newgrfs)
00794 {
00795   const LoggedAction *laend = &gamelog_action[gamelog_actions];
00796   for (const LoggedAction *la = gamelog_action; la != laend; la++) {
00797     const LoggedChange *lcend = &la->change[la->changes];
00798     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
00799       switch (lc->ct) {
00800         default: break;
00801 
00802         case GLCT_REVISION:
00803           *last_ottd_rev = lc->revision.newgrf;
00804           *ever_modified = max(*ever_modified, lc->revision.modified);
00805           break;
00806 
00807         case GLCT_GRFREM:
00808           *removed_newgrfs = true;
00809           break;
00810       }
00811     }
00812   }
00813 }