src/gamelog.cpp
branchNewGRF_ports
changeset 10991 d8811e327d12
child 10994 cd9968b6f96b
equal deleted inserted replaced
10731:67db0d431d5e 10991:d8811e327d12
       
     1 /* $Id$ */
       
     2 
       
     3 /** @file gamelog.cpp Definition of functions used for logging of important changes in the game */
       
     4 
       
     5 #include "stdafx.h"
       
     6 #include "openttd.h"
       
     7 #include "saveload.h"
       
     8 #include "core/alloc_func.hpp"
       
     9 #include "core/bitmath_func.hpp"
       
    10 #include "core/math_func.hpp"
       
    11 #include "network/core/config.h"
       
    12 #include "variables.h"
       
    13 #include "string_func.h"
       
    14 #include "settings_type.h"
       
    15 #include "newgrf_config.h"
       
    16 #include <string.h>
       
    17 #include <stdarg.h>
       
    18 #include "gamelog.h"
       
    19 #include "console_func.h"
       
    20 #include "debug.h"
       
    21 #include "rev.h"
       
    22 
       
    23 extern const uint16 SAVEGAME_VERSION;  ///< current savegame version
       
    24 
       
    25 extern SavegameType _savegame_type; ///< type of savegame we are loading
       
    26 
       
    27 extern uint32 _ttdp_version;     ///< version of TTDP savegame (if applicable)
       
    28 extern uint16 _sl_version;       ///< the major savegame version identifier
       
    29 extern byte   _sl_minor_version; ///< the minor savegame version, DO NOT USE!
       
    30 
       
    31 /** Type of logged change */
       
    32 enum GamelogChangeType {
       
    33 	GLCT_MODE,        ///< Scenario editor x Game, different landscape
       
    34 	GLCT_REVISION,    ///< Changed game revision string
       
    35 	GLCT_OLDVER,      ///< Loaded from savegame without logged data
       
    36 	GLCT_PATCH,       ///< Non-networksafe patch value changed
       
    37 	GLCT_GRFADD,      ///< Removed GRF
       
    38 	GLCT_GRFREM,      ///< Added GRF
       
    39 	GLCT_GRFCOMPAT,   ///< Loading compatible GRF
       
    40 	GLCT_GRFPARAM,    ///< GRF parameter changed
       
    41 	GLCT_GRFMOVE,     ///< GRF order changed
       
    42 	GLCT_END,         ///< So we know how many GLCTs are there
       
    43 	GLCT_NONE = 0xFF, ///< In savegames, end of list
       
    44 };
       
    45 
       
    46 
       
    47 /** Contains information about one logged change */
       
    48 struct LoggedChange {
       
    49 	GamelogChangeType ct; ///< Type of change logged in this struct
       
    50 	union {
       
    51 		struct {
       
    52 			byte mode;       ///< new game mode - Editor x Game
       
    53 			byte landscape;  ///< landscape (temperate, arctic, ...)
       
    54 		} mode;
       
    55 		struct {
       
    56 			char text[NETWORK_REVISION_LENGTH]; ///< revision string, _openttd_revision
       
    57 			uint32 newgrf;   ///< _openttd_newgrf_version
       
    58 			uint16 slver;    ///< _sl_version
       
    59 			byte modified;   ///< _openttd_revision_modified
       
    60 		} revision;
       
    61 		struct {
       
    62 			uint32 type;     ///< type of savegame, @see SavegameType
       
    63 			uint32 version;  ///< major and minor version OR ttdp version
       
    64 		} oldver;
       
    65 		GRFIdentifier grfadd;    ///< ID and md5sum of added GRF
       
    66 		struct {
       
    67 			uint32 grfid;    ///< ID of removed GRF
       
    68 		} grfrem;
       
    69 		GRFIdentifier grfcompat; ///< ID and new md5sum of changed GRF
       
    70 		struct {
       
    71 			uint32 grfid;    ///< ID of GRF with changed parameters
       
    72 		} grfparam;
       
    73 		struct {
       
    74 			uint32 grfid;    ///< ID of moved GRF
       
    75 			int32 offset;    ///< offset, positive = move down
       
    76 		} grfmove;
       
    77 		struct {
       
    78 			char *name;      ///< name of the patch
       
    79 			int32 oldval;    ///< old value
       
    80 			int32 newval;    ///< new value
       
    81 		} patch;
       
    82 	};
       
    83 };
       
    84 
       
    85 
       
    86 /** Contains information about one logged action that caused at least one logged change */
       
    87 struct LoggedAction {
       
    88 	LoggedChange *change; ///< First logged change in this action
       
    89 	uint32 changes;       ///< Number of changes in this action
       
    90 	GamelogActionType at; ///< Type of action
       
    91 	uint16 tick;          ///< Tick when it happened
       
    92 };
       
    93 
       
    94 static GamelogActionType _gamelog_action_type = GLAT_NONE; ///< action to record if anything changes
       
    95 
       
    96 static LoggedAction *_gamelog_action = NULL; ///< first logged action
       
    97 static uint _gamelog_actions         = 0;    ///< number of actions
       
    98 static LoggedAction *_current_action = NULL; ///< current action we are logging, NULL when there is no action active
       
    99 
       
   100 
       
   101 /** Stores information about new action, but doesn't allocate it
       
   102  * Action is allocated only when there is at least one change
       
   103  * @param at type of action
       
   104  */
       
   105 void GamelogStartAction(GamelogActionType at)
       
   106 {
       
   107 	assert(_gamelog_action_type == GLAT_NONE); // do not allow starting new action without stopping the previous first
       
   108 	_gamelog_action_type = at;
       
   109 }
       
   110 
       
   111 /** Stops logging of any changes
       
   112  */
       
   113 void GamelogStopAction()
       
   114 {
       
   115 	assert(_gamelog_action_type != GLAT_NONE); // nobody should try to stop if there is no action in progress
       
   116 
       
   117 	_current_action = NULL;
       
   118 	_gamelog_action_type = GLAT_NONE;
       
   119 
       
   120 	if (_debug_gamelog_level > 4) GamelogPrintDebug();
       
   121 }
       
   122 
       
   123 /** Resets and frees all memory allocated - used before loading or starting a new game
       
   124  */
       
   125 void GamelogReset()
       
   126 {
       
   127 	assert(_gamelog_action_type == GLAT_NONE);
       
   128 
       
   129 	for (uint i = 0; i < _gamelog_actions; i++) {
       
   130 		const LoggedAction *la = &_gamelog_action[i];
       
   131 		for (uint j = 0; j < la->changes; j++) {
       
   132 			const LoggedChange *lc = &la->change[j];
       
   133 			if (lc->ct == GLCT_PATCH) free(lc->patch.name);
       
   134 		}
       
   135 		free(la->change);
       
   136 	}
       
   137 
       
   138 	free(_gamelog_action);
       
   139 
       
   140 	_gamelog_action  = NULL;
       
   141 	_gamelog_actions = 0;
       
   142 	_current_action  = NULL;
       
   143 }
       
   144 
       
   145 enum {
       
   146 	GAMELOG_BUF_LEN = 1024 ///< length of buffer for one line of text
       
   147 };
       
   148 
       
   149 static int _dbgofs = 0; ///< offset in current output buffer
       
   150 
       
   151 static void AddDebugText(char *buf, const char *s, ...)
       
   152 {
       
   153 	if (GAMELOG_BUF_LEN <= _dbgofs) return;
       
   154 
       
   155 	va_list va;
       
   156 
       
   157 	va_start(va, s);
       
   158 	_dbgofs += vsnprintf(buf + _dbgofs, GAMELOG_BUF_LEN - _dbgofs, s, va);
       
   159 	va_end(va);
       
   160 }
       
   161 
       
   162 
       
   163 /** Prints GRF filename if found
       
   164  * @param grfid GRF which filename to print
       
   165  */
       
   166 static void PrintGrfFilename(char *buf, uint grfid)
       
   167 {
       
   168 	const GRFConfig *gc = FindGRFConfig(grfid);
       
   169 
       
   170 	if (gc == NULL) return;
       
   171 
       
   172 	AddDebugText(buf, ", filename: %s", gc->filename);
       
   173 }
       
   174 
       
   175 /** Prints GRF ID, checksum and filename if found
       
   176  * @param grfid GRF ID
       
   177  * @param md5sum array of md5sum to print
       
   178  */
       
   179 static void PrintGrfInfo(char *buf, uint grfid, const uint8 *md5sum)
       
   180 {
       
   181 	char txt[40];
       
   182 
       
   183 	md5sumToString(txt, lastof(txt), md5sum);
       
   184 
       
   185 	AddDebugText(buf, "GRF ID %08X, checksum %s", BSWAP32(grfid), txt);
       
   186 
       
   187 	PrintGrfFilename(buf, grfid);
       
   188 
       
   189 	return;
       
   190 }
       
   191 
       
   192 
       
   193 /** Text messages for various logged actions */
       
   194 static const char *la_text[] = {
       
   195 	"new game started",
       
   196 	"game loaded",
       
   197 	"GRF config changed",
       
   198 	"cheat was used",
       
   199 	"patch settings changed"
       
   200 };
       
   201 
       
   202 assert_compile(lengthof(la_text) == GLAT_END);
       
   203 
       
   204 
       
   205 /** Prints active gamelog */
       
   206 void GamelogPrint(GamelogPrintProc *proc)
       
   207 {
       
   208 	char buf[GAMELOG_BUF_LEN];
       
   209 
       
   210 	proc("---- gamelog start ----");
       
   211 
       
   212 	const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
       
   213 
       
   214 	for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
       
   215 		assert((uint)la->at < GLAT_END);
       
   216 
       
   217 		snprintf(buf, GAMELOG_BUF_LEN, "Tick %u: %s", (uint)la->tick, la_text[(uint)la->at]);
       
   218 		proc(buf);
       
   219 
       
   220 		const LoggedChange *lcend = &la->change[la->changes];
       
   221 
       
   222 		for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
       
   223 			_dbgofs = 0;
       
   224 			AddDebugText(buf, "     ");
       
   225 
       
   226 			switch (lc->ct) {
       
   227 				default: NOT_REACHED();
       
   228 				case GLCT_MODE:
       
   229 					AddDebugText(buf, "New game mode: %u landscape: %u",
       
   230 						(uint)lc->mode.mode, (uint)lc->mode.landscape);
       
   231 					break;
       
   232 
       
   233 				case GLCT_REVISION:
       
   234 					AddDebugText(buf, "Revision text changed to %s, savegame version %u, ",
       
   235 						lc->revision.text, lc->revision.slver);
       
   236 
       
   237 					switch (lc->revision.modified) {
       
   238 						case 0: AddDebugText(buf, "not "); break;
       
   239 						case 1: AddDebugText(buf, "maybe "); break;
       
   240 						default: break;
       
   241 					}
       
   242 
       
   243 					AddDebugText(buf, "modified, _openttd_newgrf_version = 0x%08x", lc->revision.newgrf);
       
   244 					break;
       
   245 
       
   246 				case GLCT_OLDVER:
       
   247 					AddDebugText(buf, "Conversion from ");
       
   248 					switch (lc->oldver.type) {
       
   249 						default: NOT_REACHED();
       
   250 						case SGT_OTTD:
       
   251 							AddDebugText(buf, "OTTD savegame without gamelog: version %u, %u",
       
   252 								GB(lc->oldver.version, 8, 16), GB(lc->oldver.version, 0, 8));
       
   253 							break;
       
   254 
       
   255 						case SGT_TTD:
       
   256 							AddDebugText(buf, "TTD savegame");
       
   257 							break;
       
   258 
       
   259 						case SGT_TTDP1:
       
   260 						case SGT_TTDP2:
       
   261 							AddDebugText(buf, "TTDP savegame, %s format",
       
   262 								lc->oldver.type == SGT_TTDP1 ? "old" : "new");
       
   263 							if (lc->oldver.version != 0) {
       
   264 								AddDebugText(buf, ", TTDP version %u.%u.%u.%u",
       
   265 									GB(lc->oldver.version, 24, 8), GB(lc->oldver.version, 20, 4),
       
   266 									GB(lc->oldver.version, 16, 4), GB(lc->oldver.version, 0, 16));
       
   267 							}
       
   268 							break;
       
   269 					}
       
   270 					break;
       
   271 
       
   272 				case GLCT_PATCH:
       
   273 					AddDebugText(buf, "Patch setting changed: %s : %d -> %d", lc->patch.name, lc->patch.oldval, lc->patch.newval);
       
   274 					break;
       
   275 
       
   276 				case GLCT_GRFADD:
       
   277 					AddDebugText(buf, "Added NewGRF: ");
       
   278 					PrintGrfInfo(buf, lc->grfadd.grfid, lc->grfadd.md5sum);
       
   279 					break;
       
   280 
       
   281 				case GLCT_GRFREM:
       
   282 					AddDebugText(buf, "Removed NewGRF: %08X", BSWAP32(lc->grfrem.grfid));
       
   283 					PrintGrfFilename(buf, lc->grfrem.grfid);
       
   284 					break;
       
   285 
       
   286 				case GLCT_GRFCOMPAT:
       
   287 					AddDebugText(buf, "Compatible NewGRF loaded: ");
       
   288 					PrintGrfInfo(buf, lc->grfcompat.grfid, lc->grfcompat.md5sum);
       
   289 					break;
       
   290 
       
   291 				case GLCT_GRFPARAM:
       
   292 					AddDebugText(buf, "GRF parameter changed: %08X", BSWAP32(lc->grfparam.grfid));
       
   293 					PrintGrfFilename(buf, lc->grfparam.grfid);
       
   294 					break;
       
   295 
       
   296 				case GLCT_GRFMOVE:
       
   297 					AddDebugText(buf, "GRF order changed: %08X moved %d places %s",
       
   298 						BSWAP32(lc->grfmove.grfid), abs(lc->grfmove.offset), lc->grfmove.offset >= 0 ? "down" : "up" );
       
   299 					PrintGrfFilename(buf, lc->grfmove.grfid);
       
   300 					break;
       
   301 			}
       
   302 
       
   303 			proc(buf);
       
   304 		}
       
   305 	}
       
   306 
       
   307 	proc("---- gamelog end ----");
       
   308 }
       
   309 
       
   310 
       
   311 static void GamelogPrintConsoleProc(const char *s)
       
   312 {
       
   313 	IConsolePrint(CC_WARNING, s);
       
   314 }
       
   315 
       
   316 void GamelogPrintConsole()
       
   317 {
       
   318 	GamelogPrint(&GamelogPrintConsoleProc);
       
   319 }
       
   320 
       
   321 
       
   322 static void GamelogPrintDebugProc(const char *s)
       
   323 {
       
   324 	debug_print("gamelog", s);
       
   325 }
       
   326 
       
   327 void GamelogPrintDebug()
       
   328 {
       
   329 	GamelogPrint(&GamelogPrintDebugProc);
       
   330 }
       
   331 
       
   332 
       
   333 /** Allocates new LoggedChange and new LoggedAction if needed.
       
   334  * If there is no action active, NULL is returned.
       
   335  * @param ct type of change
       
   336  * @return new LoggedChange, or NULL if there is no action active
       
   337  */
       
   338 static LoggedChange *GamelogChange(GamelogChangeType ct)
       
   339 {
       
   340 	if (_current_action == NULL) {
       
   341 		if (_gamelog_action_type == GLAT_NONE) return NULL;
       
   342 
       
   343 		_gamelog_action  = ReallocT(_gamelog_action, _gamelog_actions + 1);
       
   344 		_current_action  = &_gamelog_action[_gamelog_actions++];
       
   345 
       
   346 		_current_action->at      = _gamelog_action_type;
       
   347 		_current_action->tick    = _tick_counter;
       
   348 		_current_action->change  = NULL;
       
   349 		_current_action->changes = 0;
       
   350 	}
       
   351 
       
   352 	_current_action->change = ReallocT(_current_action->change, _current_action->changes + 1);
       
   353 
       
   354 	LoggedChange *lc = &_current_action->change[_current_action->changes++];
       
   355 	lc->ct = ct;
       
   356 
       
   357 	return lc;
       
   358 }
       
   359 
       
   360 
       
   361 /** Logs a change in game revision
       
   362  * @param revision new revision string
       
   363  */
       
   364 void GamelogRevision()
       
   365 {
       
   366 	assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
       
   367 
       
   368 	LoggedChange *lc = GamelogChange(GLCT_REVISION);
       
   369 	if (lc == NULL) return;
       
   370 
       
   371 	strncpy(lc->revision.text, _openttd_revision, lengthof(lc->revision.text));
       
   372 	lc->revision.slver = SAVEGAME_VERSION;
       
   373 	lc->revision.modified = _openttd_revision_modified;
       
   374 	lc->revision.newgrf = _openttd_newgrf_version;
       
   375 }
       
   376 
       
   377 /** Logs a change in game mode (scenario editor or game)
       
   378  */
       
   379 void GamelogMode()
       
   380 {
       
   381 	assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_CHEAT);
       
   382 
       
   383 	LoggedChange *lc = GamelogChange(GLCT_MODE);
       
   384 	if (lc == NULL) return;
       
   385 
       
   386 	lc->mode.mode      = _game_mode;
       
   387 	lc->mode.landscape = _settings_game.game_creation.landscape;
       
   388 }
       
   389 
       
   390 /** Logs loading from savegame without gamelog
       
   391  */
       
   392 void GamelogOldver()
       
   393 {
       
   394 	assert(_gamelog_action_type == GLAT_LOAD);
       
   395 
       
   396 	LoggedChange *lc = GamelogChange(GLCT_OLDVER);
       
   397 	if (lc == NULL) return;
       
   398 
       
   399 	lc->oldver.type = _savegame_type;
       
   400 	lc->oldver.version = (_savegame_type == SGT_OTTD ? ((uint32)_sl_version << 8 | _sl_minor_version) : _ttdp_version);
       
   401 }
       
   402 
       
   403 /** Logs change in game patches. Only non-networksafe patches are logged
       
   404  * @param name patch name
       
   405  * @param oldval old patch value
       
   406  * @param newval new patch value
       
   407  */
       
   408 void GamelogPatch(const char *name, int32 oldval, int32 newval)
       
   409 {
       
   410 	assert(_gamelog_action_type == GLAT_PATCH);
       
   411 
       
   412 	LoggedChange *lc = GamelogChange(GLCT_PATCH);
       
   413 	if (lc == NULL) return;
       
   414 
       
   415 	lc->patch.name = strdup(name);
       
   416 	lc->patch.oldval = oldval;
       
   417 	lc->patch.newval = newval;
       
   418 }
       
   419 
       
   420 
       
   421 /** Finds out if current revision is different than last revision stored in the savegame.
       
   422  * Appends GLCT_REVISION when the revision string changed
       
   423  */
       
   424 void GamelogTestRevision()
       
   425 {
       
   426 	const LoggedChange *rev = NULL;
       
   427 
       
   428 	const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
       
   429 	for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
       
   430 		const LoggedChange *lcend = &la->change[la->changes];
       
   431 		for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
       
   432 			if (lc->ct == GLCT_REVISION) rev = lc;
       
   433 		}
       
   434 	}
       
   435 
       
   436 	if (rev == NULL || strcmp(rev->revision.text, _openttd_revision) != 0 ||
       
   437 			rev->revision.modified != _openttd_revision_modified ||
       
   438 			rev->revision.newgrf != _openttd_newgrf_version) {
       
   439 		GamelogRevision();
       
   440 	}
       
   441 }
       
   442 
       
   443 /** Finds last stored game mode or landscape.
       
   444  * Any change is logged
       
   445  */
       
   446 void GamelogTestMode()
       
   447 {
       
   448 	const LoggedChange *mode = NULL;
       
   449 
       
   450 	const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
       
   451 	for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
       
   452 		const LoggedChange *lcend = &la->change[la->changes];
       
   453 		for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
       
   454 			if (lc->ct == GLCT_MODE) mode = lc;
       
   455 		}
       
   456 	}
       
   457 
       
   458 	if (mode == NULL || mode->mode.mode != _game_mode || mode->mode.landscape != _settings_game.game_creation.landscape) GamelogMode();
       
   459 }
       
   460 
       
   461 
       
   462 /** Decides if GRF should be logged
       
   463  * @param g grf to determine
       
   464  * @return true iff GRF is not static and is loaded
       
   465  */
       
   466 static inline bool IsLoggableGrfConfig(const GRFConfig *g)
       
   467 {
       
   468 	return !HasBit(g->flags, GCF_STATIC) && g->status != GCS_NOT_FOUND;
       
   469 }
       
   470 
       
   471 /** Logs removal of a GRF
       
   472  * @param grfid ID of removed GRF
       
   473  */
       
   474 void GamelogGRFRemove(uint32 grfid)
       
   475 {
       
   476 	assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
       
   477 
       
   478 	LoggedChange *lc = GamelogChange(GLCT_GRFREM);
       
   479 	if (lc == NULL) return;
       
   480 
       
   481 	lc->grfrem.grfid = grfid;
       
   482 }
       
   483 
       
   484 /** Logs adding of a GRF
       
   485  * @param newg added GRF
       
   486  */
       
   487 void GamelogGRFAdd(const GRFConfig *newg)
       
   488 {
       
   489 	assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_GRF);
       
   490 
       
   491 	if (!IsLoggableGrfConfig(newg)) return;
       
   492 
       
   493 	LoggedChange *lc = GamelogChange(GLCT_GRFADD);
       
   494 	if (lc == NULL) return;
       
   495 
       
   496 	memcpy(&lc->grfadd, newg, sizeof(GRFIdentifier));
       
   497 }
       
   498 
       
   499 /** Logs loading compatible GRF
       
   500  * (the same ID, but different MD5 hash)
       
   501  * @param newg new (updated) GRF
       
   502  */
       
   503 void GamelogGRFCompatible(const GRFIdentifier *newg)
       
   504 {
       
   505 	assert(_gamelog_action_type == GLAT_LOAD || _gamelog_action_type == GLAT_GRF);
       
   506 
       
   507 	LoggedChange *lc = GamelogChange(GLCT_GRFCOMPAT);
       
   508 	if (lc == NULL) return;
       
   509 
       
   510 	memcpy(&lc->grfcompat, newg, sizeof(GRFIdentifier));
       
   511 }
       
   512 
       
   513 /** Logs changing GRF order
       
   514  * @param grfid GRF that is moved
       
   515  * @param offset how far it is moved, positive = moved down
       
   516  */
       
   517 static void GamelogGRFMove(uint32 grfid, int32 offset)
       
   518 {
       
   519 	assert(_gamelog_action_type == GLAT_GRF);
       
   520 
       
   521 	LoggedChange *lc = GamelogChange(GLCT_GRFMOVE);
       
   522 	if (lc == NULL) return;
       
   523 
       
   524 	lc->grfmove.grfid  = grfid;
       
   525 	lc->grfmove.offset = offset;
       
   526 }
       
   527 
       
   528 /** Logs change in GRF parameters.
       
   529  * Details about parameters changed are not stored
       
   530  * @param grfid ID of GRF to store
       
   531  */
       
   532 static void GamelogGRFParameters(uint32 grfid)
       
   533 {
       
   534 	assert(_gamelog_action_type == GLAT_GRF);
       
   535 
       
   536 	LoggedChange *lc = GamelogChange(GLCT_GRFPARAM);
       
   537 	if (lc == NULL) return;
       
   538 
       
   539 	lc->grfparam.grfid = grfid;
       
   540 }
       
   541 
       
   542 /** Logs adding of list of GRFs.
       
   543  * Useful when old savegame is loaded or when new game is started
       
   544  * @param newg head of GRF linked list
       
   545  */
       
   546 void GamelogGRFAddList(const GRFConfig *newg)
       
   547 {
       
   548 	assert(_gamelog_action_type == GLAT_START || _gamelog_action_type == GLAT_LOAD);
       
   549 
       
   550 	for (; newg != NULL; newg = newg->next) {
       
   551 		GamelogGRFAdd(newg);
       
   552 	}
       
   553 }
       
   554 
       
   555 /** List of GRFs using array of pointers instead of linked list */
       
   556 struct GRFList {
       
   557 	uint n;
       
   558 	const GRFConfig *grf[VARARRAY_SIZE];
       
   559 };
       
   560 
       
   561 /** Generates GRFList
       
   562  * @param grfc head of GRF linked list
       
   563  */
       
   564 static GRFList *GenerateGRFList(const GRFConfig *grfc)
       
   565 {
       
   566 	uint n = 0;
       
   567 	for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
       
   568 		if (IsLoggableGrfConfig(g)) n++;
       
   569 	}
       
   570 
       
   571 	GRFList *list = (GRFList*)MallocT<byte>(sizeof(GRFList) + n * sizeof(GRFConfig*));
       
   572 
       
   573 	list->n = 0;
       
   574 	for (const GRFConfig *g = grfc; g != NULL; g = g->next) {
       
   575 		if (IsLoggableGrfConfig(g)) list->grf[list->n++] = g;
       
   576 	}
       
   577 
       
   578 	return list;
       
   579 }
       
   580 
       
   581 /** Compares two NewGRF lists and logs any change
       
   582  * @param oldc original GRF list
       
   583  * @param newc new GRF list
       
   584  */
       
   585 void GamelogGRFUpdate(const GRFConfig *oldc, const GRFConfig *newc)
       
   586 {
       
   587 	GRFList *ol = GenerateGRFList(oldc);
       
   588 	GRFList *nl = GenerateGRFList(newc);
       
   589 
       
   590 	uint o = 0, n = 0;
       
   591 
       
   592 	while (o < ol->n && n < nl->n) {
       
   593 		const GRFConfig *og = ol->grf[o];
       
   594 		const GRFConfig *ng = nl->grf[n];
       
   595 
       
   596 		if (og->grfid != ng->grfid) {
       
   597 			uint oi, ni;
       
   598 			for (oi = 0; oi < ol->n; oi++) {
       
   599 				if (ol->grf[oi]->grfid == nl->grf[n]->grfid) break;
       
   600 			}
       
   601 			if (oi < o) {
       
   602 				/* GRF was moved, this change has been logged already */
       
   603 				n++;
       
   604 				continue;
       
   605 			}
       
   606 			if (oi == ol->n) {
       
   607 				/* GRF couldn't be found in the OLD list, GRF was ADDED */
       
   608 				GamelogGRFAdd(nl->grf[n++]);
       
   609 				continue;
       
   610 			}
       
   611 			for (ni = 0; ni < nl->n; ni++) {
       
   612 				if (nl->grf[ni]->grfid == ol->grf[o]->grfid) break;
       
   613 			}
       
   614 			if (ni < n) {
       
   615 				/* GRF was moved, this change has been logged already */
       
   616 				o++;
       
   617 				continue;
       
   618 			}
       
   619 			if (ni == nl->n) {
       
   620 				/* GRF couldn't be found in the NEW list, GRF was REMOVED */
       
   621 				GamelogGRFRemove(ol->grf[o++]->grfid);
       
   622 				continue;
       
   623 			}
       
   624 
       
   625 			/* o < oi < ol->n
       
   626 			 * n < ni < nl->n */
       
   627 			assert(ni > n && ni < nl->n);
       
   628 			assert(oi > o && oi < ol->n);
       
   629 
       
   630 			ni -= n; // number of GRFs it was moved downwards
       
   631 			oi -= o; // number of GRFs it was moved upwards
       
   632 
       
   633 			if (ni >= oi) { // prefer the one that is moved further
       
   634 				/* GRF was moved down */
       
   635 				GamelogGRFMove(ol->grf[o++]->grfid, ni);
       
   636 			} else {
       
   637 				GamelogGRFMove(nl->grf[n++]->grfid, -(int)oi);
       
   638 			}
       
   639 		} else {
       
   640 			if (memcmp(og->md5sum, ng->md5sum, sizeof(og->md5sum)) != 0) {
       
   641 				/* md5sum changed, probably loading 'compatible' GRF */
       
   642 				GamelogGRFCompatible(nl->grf[n]);
       
   643 			}
       
   644 
       
   645 			if (og->num_params != ng->num_params || memcmp(og->param, ng->param, og->num_params * sizeof(og->param[0])) != 0) {
       
   646 				GamelogGRFParameters(ol->grf[o]->grfid);
       
   647 			}
       
   648 
       
   649 			o++;
       
   650 			n++;
       
   651 		}
       
   652 	}
       
   653 
       
   654 	while (o < ol->n) GamelogGRFRemove(ol->grf[o++]->grfid); // remaining GRFs were removed ...
       
   655 	while (n < nl->n) GamelogGRFAdd   (nl->grf[n++]);    // ... or added
       
   656 
       
   657 	free(ol);
       
   658 	free(nl);
       
   659 }
       
   660 
       
   661 
       
   662 static const SaveLoad _glog_action_desc[] = {
       
   663 	SLE_VAR(LoggedAction, tick,              SLE_UINT16),
       
   664 	SLE_END()
       
   665 };
       
   666 
       
   667 static const SaveLoad _glog_mode_desc[] = {
       
   668 	SLE_VAR(LoggedChange, mode.mode,         SLE_UINT8),
       
   669 	SLE_VAR(LoggedChange, mode.landscape,    SLE_UINT8),
       
   670 	SLE_END()
       
   671 };
       
   672 
       
   673 static const SaveLoad _glog_revision_desc[] = {
       
   674 	SLE_ARR(LoggedChange, revision.text,     SLE_UINT8,  NETWORK_REVISION_LENGTH),
       
   675 	SLE_VAR(LoggedChange, revision.newgrf,   SLE_UINT32),
       
   676 	SLE_VAR(LoggedChange, revision.slver,    SLE_UINT16),
       
   677 	SLE_VAR(LoggedChange, revision.modified, SLE_UINT8),
       
   678 	SLE_END()
       
   679 };
       
   680 
       
   681 static const SaveLoad _glog_oldver_desc[] = {
       
   682 	SLE_VAR(LoggedChange, oldver.type,       SLE_UINT32),
       
   683 	SLE_VAR(LoggedChange, oldver.version,    SLE_UINT32),
       
   684 	SLE_END()
       
   685 };
       
   686 
       
   687 static const SaveLoad _glog_patch_desc[] = {
       
   688 	SLE_STR(LoggedChange, patch.name,        SLE_STR,    128),
       
   689 	SLE_VAR(LoggedChange, patch.oldval,      SLE_INT32),
       
   690 	SLE_VAR(LoggedChange, patch.newval,      SLE_INT32),
       
   691 	SLE_END()
       
   692 };
       
   693 
       
   694 static const SaveLoad _glog_grfadd_desc[] = {
       
   695 	SLE_VAR(LoggedChange, grfadd.grfid,      SLE_UINT32    ),
       
   696 	SLE_ARR(LoggedChange, grfadd.md5sum,     SLE_UINT8,  16),
       
   697 	SLE_END()
       
   698 };
       
   699 
       
   700 static const SaveLoad _glog_grfrem_desc[] = {
       
   701 	SLE_VAR(LoggedChange, grfrem.grfid,      SLE_UINT32),
       
   702 	SLE_END()
       
   703 };
       
   704 
       
   705 static const SaveLoad _glog_grfcompat_desc[] = {
       
   706 	SLE_VAR(LoggedChange, grfcompat.grfid,   SLE_UINT32    ),
       
   707 	SLE_ARR(LoggedChange, grfcompat.md5sum,  SLE_UINT8,  16),
       
   708 	SLE_END()
       
   709 };
       
   710 
       
   711 static const SaveLoad _glog_grfparam_desc[] = {
       
   712 	SLE_VAR(LoggedChange, grfparam.grfid,    SLE_UINT32),
       
   713 	SLE_END()
       
   714 };
       
   715 
       
   716 static const SaveLoad _glog_grfmove_desc[] = {
       
   717 	SLE_VAR(LoggedChange, grfmove.grfid,    SLE_UINT32),
       
   718 	SLE_VAR(LoggedChange, grfmove.offset,   SLE_INT32),
       
   719 	SLE_END()
       
   720 };
       
   721 
       
   722 static const SaveLoad *_glog_desc[] = {
       
   723 	_glog_mode_desc,
       
   724 	_glog_revision_desc,
       
   725 	_glog_oldver_desc,
       
   726 	_glog_patch_desc,
       
   727 	_glog_grfadd_desc,
       
   728 	_glog_grfrem_desc,
       
   729 	_glog_grfcompat_desc,
       
   730 	_glog_grfparam_desc,
       
   731 	_glog_grfmove_desc
       
   732 };
       
   733 
       
   734 assert_compile(lengthof(_glog_desc) == GLCT_END);
       
   735 
       
   736 static void Load_GLOG()
       
   737 {
       
   738 	assert(_gamelog_action == NULL);
       
   739 	assert(_gamelog_actions == 0);
       
   740 
       
   741 	GamelogActionType at;
       
   742 	while ((at = (GamelogActionType)SlReadByte()) != GLAT_NONE) {
       
   743 		_gamelog_action = ReallocT(_gamelog_action, _gamelog_actions + 1);
       
   744 		LoggedAction *la = &_gamelog_action[_gamelog_actions++];
       
   745 
       
   746 		la->at = at;
       
   747 
       
   748 		SlObject(la, _glog_action_desc); // has to be saved after 'DATE'!
       
   749 		la->change = NULL;
       
   750 		la->changes = 0;
       
   751 
       
   752 		GamelogChangeType ct;
       
   753 		while ((ct = (GamelogChangeType)SlReadByte()) != GLCT_NONE) {
       
   754 			la->change = ReallocT(la->change, la->changes + 1);
       
   755 
       
   756 			LoggedChange *lc = &la->change[la->changes++];
       
   757 			lc->ct = ct;
       
   758 
       
   759 			assert((uint)ct < GLCT_END);
       
   760 
       
   761 			SlObject(lc, _glog_desc[ct]);
       
   762 		}
       
   763 	}
       
   764 }
       
   765 
       
   766 static void Save_GLOG()
       
   767 {
       
   768 	const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
       
   769 	size_t length = 0;
       
   770 
       
   771 	for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
       
   772 		const LoggedChange *lcend = &la->change[la->changes];
       
   773 		for (LoggedChange *lc = la->change; lc != lcend; lc++) {
       
   774 			assert((uint)lc->ct < lengthof(_glog_desc));
       
   775 			length += SlCalcObjLength(lc, _glog_desc[lc->ct]) + 1;
       
   776 		}
       
   777 		length += 4;
       
   778 	}
       
   779 	length++;
       
   780 
       
   781 	SlSetLength(length);
       
   782 
       
   783 	for (LoggedAction *la = _gamelog_action; la != laend; la++) {
       
   784 		SlWriteByte(la->at);
       
   785 		SlObject(la, _glog_action_desc);
       
   786 
       
   787 		const LoggedChange *lcend = &la->change[la->changes];
       
   788 		for (LoggedChange *lc = la->change; lc != lcend; lc++) {
       
   789 			SlWriteByte(lc->ct);
       
   790 			assert((uint)lc->ct < GLCT_END);
       
   791 			SlObject(lc, _glog_desc[lc->ct]);
       
   792 		}
       
   793 		SlWriteByte(GLCT_NONE);
       
   794 	}
       
   795 	SlWriteByte(GLAT_NONE);
       
   796 }
       
   797 
       
   798 
       
   799 extern const ChunkHandler _gamelog_chunk_handlers[] = {
       
   800 	{ 'GLOG', Save_GLOG, Load_GLOG, CH_RIFF | CH_LAST }
       
   801 };