~ubuntu-branches/debian/squeeze/openttd/squeeze

« back to all changes in this revision

Viewing changes to src/gamelog.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jordi Mallach, Matthijs Kooijman, Jordi Mallach
  • Date: 2009-04-15 18:22:10 UTC
  • mfrom: (1.1.6 upstream) (2.1.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090415182210-22ktb8kdbp2tf3bm
[ Matthijs Kooijman ]
* New upstream release.
* Remove Debian specific desktop file, upstream provides one now. 
* Add debian/watch file.

[ Jordi Mallach ]
* Bump Standards-Version to 3.8.1, with no changes required.
* Move to debhelper compat 7. Bump Build-Depends accordingly.
* Use dh_prep.
* Add "set -e" to config script.
* Remove a few extra doc files that get installed by upstream Makefile.
* Add more complete copyright information.

Show diffs side-by-side

added added

removed removed

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