|
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 }; |