# 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 GamelogReset()
    00070 {
    00071   assert(_gamelog_action_type == GLAT_NONE);
    00072
    00073   for (uint i = 0; i < _gamelog_actions; i++) {
    00074     const LoggedAction *la = &_gamelog_action[i];
    00075     for (uint j = 0; j < la->changes; j++) {
    00076       const LoggedChange *lc = &la->change[j];
    00077       if (lc->ct == GLCT_SETTING) free(lc->setting.name);
    00078     }
    00079     free(la->change);
    00080   }
    00081
    00082   free(_gamelog_action);
    00083
    00084   _gamelog_action  = NULL;
    00085   _gamelog_actions = 0;
    00086   _current_action  = NULL;
    00087 }
    00088
    00089 static const uint GAMELOG_BUF_LEN = 1024;
    00090
    00091 static uint _dbgofs = 0;
    00092
    00093 static void AddDebugText(char *buf, const char *s, ...) WARN_FORMAT(2, 3);
    00094
    00095 static void AddDebugText(char *buf, const char *s, ...)
    00096 {
    00097   if (GAMELOG_BUF_LEN <= _dbgofs) return;
    00098
    00099   va_list va;
    00100
    00101   va_start(va, s);
    00102   _dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
    00103   va_end(va);
    00104 }
    00105
    00106
    00114 static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum, const GRFConfig *gc)
    00115 {
    00116   char txt[40];
    00117
    00118   if (md5sum != NULL) {
    00119     md5sumToString(txt, lastof(txt), md5sum);
    00120     AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
    00121   } else {
    00122     AddDebugText(buf, "GRF ID %08X", BSWAP32(grfid));
    00123   }
    00124
    00125   if (gc != NULL) {
    00126     AddDebugText(buf, ", filename: %s (md5sum matches)", gc->filename);
    00127   } else {
    00128     gc = FindGRFConfig(grfid, FGCM_ANY);
    00129     if (gc != NULL) {
    00130       AddDebugText(buf, ", filename: %s (matches GRFID only)", gc->filename);
    00131     } else {
    00132       AddDebugText(buf, ", unknown GRF");
    00133     }
    00134   }
    00135   return;
    00136 }
    00137
    00138
    00140 static const char * const la_text[] = {
    00141   "new game started",
    00142   "game loaded",
    00143   "GRF config changed",
    00144   "cheat was used",
    00145   "settings changed",
    00146   "GRF bug triggered",
    00147   "emergency savegame",
    00148 };
    00149
    00150 assert_compile(lengthof(la_text) == GLAT_END);
    00151
    00159 struct GRFPresence{
    00160   const GRFConfig *gc;
    00161   bool was_missing;
    00162
    00163   GRFPresence(const GRFConfig *gc) : gc(gc), was_missing(false) {}
    00164 };
    00165 typedef SmallMap<uint32, GRFPresence> GrfIDMapping;
    00166
    00171 void GamelogPrint(GamelogPrintProc *proc)
    00172 {
    00173   char buf[GAMELOG_BUF_LEN];
    00174   GrfIDMapping grf_names;
    00175
    00176   proc("---- gamelog start ----");
    00177
    00178   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00179
    00180   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00181     assert((uint)la->at < GLAT_END);
    00182
    00183     snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
    00184     proc(buf);
    00185
    00186     const LoggedChange *lcend = &la->change[la->changes];
    00187
    00188     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00189       _dbgofs = 0;
    00190       AddDebugText(buf, "     ");
    00191
    00192       switch (lc->ct) {
    00193         default: NOT_REACHED();
    00194         case GLCT_MODE:
    00195           AddDebugText(buf, "New game mode: %u landscape: %u",
    00196             (uint)lc->mode.mode, (uint)lc->mode.landscape);
    00197           break;
    00198
    00199         case GLCT_REVISION:
    00200           AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
    00201             lc->revision.text, lc->revision.slver);
    00202
    00203           switch (lc->revision.modified) {
    00204             case 0: AddDebugText(buf, "not "); break;
    00205             case 1: AddDebugText(buf, "maybe "); break;
    00206             default: break;
    00207           }
    00208
    00209           AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
    00210           break;
    00211
    00212         case GLCT_OLDVER:
    00213           AddDebugText(buf, "Conversion from ");
    00214           switch (lc->oldver.type) {
    00215             default: NOT_REACHED();
    00216             case SGT_OTTD:
    00217               AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
    00218                 GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
    00219               break;
    00220
    00221             case SGT_TTO:
    00222               AddDebugText(buf, "TTO savegame");
    00223               break;
    00224
    00225             case SGT_TTD:
    00226               AddDebugText(buf, "TTD savegame");
    00227               break;
    00228
    00229             case SGT_TTDP1:
    00230             case SGT_TTDP2:
    00231               AddDebugText(buf, "TTDP savegame, %s format",
    00232                 lc->oldver.type == SGT_TTDP1 ? "old" : "new");
    00233               if (lc->oldver.version != 0) {
    00234                 AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
    00235                   GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
    00236                   GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
    00237               }
    00238               break;
    00239           }
    00240           break;
    00241
    00242         case GLCT_SETTING:
    00243           AddDebugText(buf, "Setting changed: %s : %d -> %d", lc->setting.name, lc->setting.oldval, lc->setting.newval);
    00244           break;
    00245
    00246         case GLCT_GRFADD: {
    00247           const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum);
    00248           AddDebugText(buf, "Added NewGRF: ");
    00249           PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum, gc);
    00250           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
    00251           if (gm != grf_names.End() && !gm->second.was_missing) AddDebugText(buf, ". Gamelog inconsistency: GrfID was already added!");
    00252           grf_names[lc->grfadd.grfid] = gc;
    00253           break;
    00254         }
    00255
    00256         case GLCT_GRFREM: {
    00257           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
    00258           AddDebugText(buf, la->at == GLAT_LOAD ? "Missing NewGRF: " : "Removed NewGRF: ");
    00259           PrintGrfInfo(buf, lc->grfrem.grfid, NULL, gm != grf_names.End() ? gm->second.gc : NULL);
    00260           if (gm == grf_names.End()) {
    00261             AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
    00262           } else {
    00263             if (la->at == GLAT_LOAD) {
    00264               /* Missing grfs on load are not removed from the configuration */
    00265               gm->second.was_missing = true;
    00266             } else {
    00267               grf_names.Erase(gm);
    00268             }
    00269           }
    00270           break;
    00271         }
    00272
    00273         case GLCT_GRFCOMPAT: {
    00274           const GRFConfig *gc = FindGRFConfig(lc->grfadd.grfid, FGCM_EXACT, lc->grfadd.md5sum);
    00275           AddDebugText(buf, "Compatible NewGRF loaded: ");
    00276           PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum, gc);
    00277           if (!grf_names.Contains(lc->grfcompat.grfid)) AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
    00278           grf_names[lc->grfcompat.grfid] = gc;
    00279           break;
    00280         }
    00281
    00282         case GLCT_GRFPARAM: {
    00283           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
    00284           AddDebugText(buf, "GRF parameter changed: ");
    00285           PrintGrfInfo(buf, lc->grfparam.grfid, NULL, gm != grf_names.End() ? gm->second.gc : NULL);
    00286           if (gm == grf_names.End()) AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
    00287           break;
    00288         }
    00289
    00290         case GLCT_GRFMOVE: {
    00291           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
    00292           AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
    00293             BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
    00294           PrintGrfInfo(buf, lc->grfmove.grfid, NULL, gm != grf_names.End() ? gm->second.gc : NULL);
    00295           if (gm == grf_names.End()) AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
    00296           break;
    00297         }
    00298
    00299         case GLCT_GRFBUG: {
    00300           GrfIDMapping::Pair *gm = grf_names.Find(lc->grfrem.grfid);
    00301           switch (lc->grfbug.bug) {
    00302             default: NOT_REACHED();
    00303             case GBUG_VEH_LENGTH:
    00304               AddDebugText(buf, "Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X", BSWAP32(lc->grfbug.grfid), (uint)lc->grfbug.data);
    00305               break;
    00306           }
    00307           PrintGrfInfo(buf, lc->grfbug.grfid, NULL, gm != grf_names.End() ? gm->second.gc : NULL);
    00308           if (gm == grf_names.End()) AddDebugText(buf, ". Gamelog inconsistency: GrfID was never added!");
    00309           break;
    00310         }
    00311
    00312         case GLCT_EMERGENCY:
    00313           break;
    00314       }
    00315
    00316       proc(buf);
    00317     }
    00318   }
    00319
    00320   proc("---- gamelog end ----");
    00321 }
    00322
    00323
    00324 static void GamelogPrintConsoleProc(const char *s)
    00325 {
    00326   IConsolePrint(CC_WARNING, s);
    00327 }
    00328
    00329 void GamelogPrintConsole()
    00330 {
    00331   GamelogPrint(&GamelogPrintConsoleProc);
    00332 }
    00333
    00334 static int _gamelog_print_level = 0;
    00335
    00336 static void GamelogPrintDebugProc(const char *s)
    00337 {
    00338   DEBUG(gamelog, _gamelog_print_level, "%s", s);
    00339 }
    00340
    00341
    00348 void GamelogPrintDebug(int level)
    00349 {
    00350   _gamelog_print_level = level;
    00351   GamelogPrint(&GamelogPrintDebugProc);
    00352 }
    00353
    00354
    00361 static LoggedChange *GamelogChange(GamelogChangeType ct)
    00362 {
    00363   if (_current_action == NULL) {
    00364     if (_gamelog_action_type == GLAT_NONE) return NULL;
    00365
    00366     _gamelog_action  = ReallocT(_gamelog_action, _gamelog_actions + 1);
    00367     _current_action  = &_gamelog_action[_gamelog_actions++];
    00368
    00369     _current_action->at      = _gamelog_action_type;
    00370     _current_action->tick    = _tick_counter;
    00371     _current_action->change  = NULL;
    00372     _current_action->changes = 0;
    00373   }
    00374
    00375   _current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
    00376
    00377   LoggedChange *lc = &_current_action->change[_current_action->changes++];
    00378   lc->ct = ct;
    00379
    00380   return lc;
    00381 }
    00382
    00383
    00387 void GamelogEmergency()
    00388 {
    00389   /* Terminate any active action */
    00390   if (_gamelog_action_type != GLAT_NONE) GamelogStopAction();
    00391   GamelogStartAction(GLAT_EMERGENCY);
    00392   GamelogChange(GLCT_EMERGENCY);
    00393   GamelogStopAction();
    00394 }
    00395
    00399 bool GamelogTestEmergency()
    00400 {
    00401   const LoggedChange *emergency = NULL;
    00402
    00403   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00404   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00405     const LoggedChange *lcend = &la->change[la->changes];
    00406     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00407       if (lc->ct == GLCT_EMERGENCY) emergency = lc;
    00408     }
    00409   }
    00410
    00411   return (emergency != NULL);
    00412 }
    00413
    00417 void GamelogRevision()
    00418 {
    00419   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
    00420
    00421   LoggedChange *lc = GamelogChange(GLCT_REVISION);
    00422   if (lc == NULL) return;
    00423
    00424   memset(lc->revision.text, 0, sizeof(lc->revision.text));
    00425   strecpy(lc->revision.text, _openttd_revision, lastof(lc->revision.text));
    00426   lc->revision.slver = SAVEGAME_VERSION;
    00427   lc->revision.modified = _openttd_revision_modified;
    00428   lc->revision.newgrf = _openttd_newgrf_version;
    00429 }
    00430
    00434 void GamelogMode()
    00435 {
    00436   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
    00437
    00438   LoggedChange *lc = GamelogChange(GLCT_MODE);
    00439   if (lc == NULL) return;
    00440
    00441   lc->mode.mode      = _game_mode;
    00442   lc->mode.landscape = _settings_game.game_creation.landscape;
    00443 }
    00444
    00448 void GamelogOldver()
    00449 {
    00450   assert(_gamelog_action_type == GLAT_LOAD);
    00451
    00452   LoggedChange *lc = GamelogChange(GLCT_OLDVER);
    00453   if (lc == NULL) return;
    00454
    00455   lc->oldver.type = _savegame_type;
    00456   lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
    00457 }
    00458
    00465 void GamelogSetting(const char *name, int32 oldval, int32 newval)
    00466 {
    00467   assert(_gamelog_action_type == GLAT_SETTING);
    00468
    00469   LoggedChange *lc = GamelogChange(GLCT_SETTING);
    00470   if (lc == NULL) return;
    00471
    00472   lc->setting.name = strdup(name);
    00473   lc->setting.oldval = oldval;
    00474   lc->setting.newval = newval;
    00475 }
    00476
    00477
    00482 void GamelogTestRevision()
    00483 {
    00484   const LoggedChange *rev = NULL;
    00485
    00486   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00487   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00488     const LoggedChange *lcend = &la->change[la->changes];
    00489     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00490       if (lc->ct == GLCT_REVISION) rev = lc;
    00491     }
    00492   }
    00493
    00494   if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
    00495       rev->revision.modified != _openttd_revision_modified ||
    00496       rev->revision.newgrf != _openttd_newgrf_version) {
    00497     GamelogRevision();
    00498   }
    00499 }
    00500
    00505 void GamelogTestMode()
    00506 {
    00507   const LoggedChange *mode = NULL;
    00508
    00509   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00510   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00511     const LoggedChange *lcend = &la->change[la->changes];
    00512     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00513       if (lc->ct == GLCT_MODE) mode = lc;
    00514     }
    00515   }
    00516
    00517   if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
    00518 }
    00519
    00520
    00527 static void GamelogGRFBug(uint32 grfid, byte bug, uint64 data)
    00528 {
    00529   assert(_gamelog_action_type == GLAT_GRFBUG);
    00530
    00531   LoggedChange *lc = GamelogChange(GLCT_GRFBUG);
    00532   if (lc == NULL) return;
    00533
    00534   lc->grfbug.data  = data;
    00535   lc->grfbug.grfid = grfid;
    00536   lc->grfbug.bug   = bug;
    00537 }
    00538
    00548 bool GamelogGRFBugReverse(uint32 grfid, uint16 internal_id)
    00549 {
    00550   const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
    00551   for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
    00552     const LoggedChange *lcend = &la->change[la->changes];
    00553     for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
    00554       if (lc->ct == GLCT_GRFBUG && lc->grfbug.grfid == grfid &&
    00555           lc->grfbug.bug == GBUG_VEH_LENGTH && lc->grfbug.data == internal_id) {
    00556         return false;
    00557       }
    00558     }
    00559   }
    00560
    00561   GamelogStartAction(GLAT_GRFBUG);
    00562   GamelogGRFBug(grfid, GBUG_VEH_LENGTH, internal_id);
    00563   GamelogStopAction();
    00564
    00565   return true;
    00566 }
    00567
    00568
    00574 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
    00575 {
    00576   return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
    00577 }
    00578
    00583 void GamelogGRFRemove(uint32 grfid)
    00584 {
    00585   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
    00586
    00587   LoggedChange *lc = GamelogChange(GLCT_GRFREM);
    00588   if (lc == NULL) return;
    00589
    00590   lc->grfrem.grfid = grfid;
    00591 }
    00592
    00597 void GamelogGRFAdd(const GRFConfig *newg)
    00598 {
    00599   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
    00600
    00601   if (!IsLoggableGrfConfig(newg)) return;
    00602
    00603   LoggedChange *lc = GamelogChange(GLCT_GRFADD);
    00604   if (lc == NULL) return;
    00605
    00606   lc->grfadd = newg->ident;
    00607 }
    00608
    00614 void GamelogGRFCompatible(const GRFIdentifier *newg)
    00615 {
    00616   assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
    00617
    00618   LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
    00619   if (lc == NULL) return;
    00620
    00621   lc->grfcompat = *newg;
    00622 }
    00623
    00629 static void GamelogGRFMove(uint32 grfid, int32 offset)
    00630 {
    00631   assert(_gamelog_action_type == GLAT_GRF);
    00632
    00633   LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
    00634   if (lc == NULL) return;
    00635
    00636   lc->grfmove.grfid  = grfid;
    00637   lc->grfmove.offset = offset;
    00638 }
    00639
    00645 static void GamelogGRFParameters(uint32 grfid)
    00646 {
    00647   assert(_gamelog_action_type == GLAT_GRF);
    00648
    00649   LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
    00650   if (lc == NULL) return;
    00651
    00652   lc->grfparam.grfid = grfid;
    00653 }
    00654
    00660 void GamelogGRFAddList(const GRFConfig *newg)
    00661 {
    00662   assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
    00663
    00664   for (; newg != NULL; newg = newg->next) {
    00665     GamelogGRFAdd(newg);
    00666   }
    00667 }
    00668
    00670 struct GRFList {
    00671   uint n;
    00672   const GRFConfig *grf[];
    00673 };
    00674
    00679 static GRFList *GenerateGRFList(const GRFConfig *grfc)
    00680 {
    00681   uint n = 0;
    00682   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
    00683     if (IsLoggableGrfConfig(g)) n++;
    00684   }
    00685
    00686   GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
    00687
    00688   list->n = 0;
    00689   for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
    00690     if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
    00691   }
    00692
    00693   return list;
    00694 }
    00695
    00701 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
    00702 {
    00703   GRFList *ol = GenerateGRFList(oldc);
    00704   GRFList *nl = GenerateGRFList(newc);
    00705
    00706   uint o = 0, n = 0;
    00707
    00708   while (o < ol->n && n < nl->n) {
    00709     const GRFConfig *og = ol->grf[o];
    00710     const GRFConfig *ng = nl->grf[n];
    00711
    00712     if (og->ident.grfid != ng->ident.grfid) {
    00713       uint oi, ni;
    00714       for (oi = 0; oi < ol->n; oi++) {
    00715         if (ol->grf[oi]->ident.grfid == nl->grf[n]->ident.grfid) break;
    00716       }
    00717       if (oi < o) {
    00718         /* GRF was moved, this change has been logged already */
    00719         n++;
    00720         continue;
    00721       }
    00722       if (oi == ol->n) {
    00723         /* GRF couldn't be found in the OLD list, GRF was ADDED */
    00724         GamelogGRFAdd(nl->grf[n++]);
    00725         continue;
    00726       }
    00727       for (ni = 0; ni < nl->n; ni++) {
    00728         if (nl->grf[ni]->ident.grfid == ol->grf[o]->ident.grfid) break;
    00729       }
    00730       if (ni < n) {
    00731         /* GRF was moved, this change has been logged already */
    00732         o++;
    00733         continue;
    00734       }
    00735       if (ni == nl->n) {
    00736         /* GRF couldn't be found in the NEW list, GRF was REMOVED */
    00737         GamelogGRFRemove(ol->grf[o++]->ident.grfid);
    00738         continue;
    00739       }
    00740
    00741       /* o < oi < ol->n
    00742        * n < ni < nl->n */
    00743       assert(ni > n && ni < nl->n);
    00744       assert(oi > o && oi < ol->n);
    00745
    00746       ni -= n; // number of GRFs it was moved downwards
    00747       oi -= o; // number of GRFs it was moved upwards
    00748
    00749       if (ni >= oi) { // prefer the one that is moved further
    00750         /* GRF was moved down */
    00751         GamelogGRFMove(ol->grf[o++]->ident.grfid, ni);
    00752       } else {
    00753         GamelogGRFMove(nl->grf[n++]->ident.grfid, -(int)oi);
    00754       }
    00755     } else {
    00756       if (memcmp(og->ident.md5sum, ng->ident.md5sum, sizeof(og->ident.md5sum)) != 0) {
    00757         /* md5sum changed, probably loading 'compatible' GRF */
    00758         GamelogGRFCompatible(&nl->grf[n]->ident);
    00759       }
    00760
    00761       if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
    00762         GamelogGRFParameters(ol->grf[o]->ident.grfid);
    00763       }
    00764
    00765       o++;
    00766       n++;
    00767     }
    00768   }
    00769
    00770   while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->ident.grfid); // remaining GRFs were removed ...
    00771   while (n < nl->n) GamelogGRFAdd   (nl->grf[n++]);              // ... or added
    00772
    00773   free(ol);
    00774   free(nl);
    00775 }