# 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 "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 }