2009-08-21 20:21:05 +00:00
/*
* This file is part of OpenTTD .
* 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.
* 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 .
* 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/>.
*/
2008-06-03 18:35:58 +00:00
/** @file gamelog.cpp Definition of functions used for logging of important changes in the game */
# include "stdafx.h"
2009-01-04 15:32:25 +00:00
# include "saveload/saveload.h"
2008-06-03 18:35:58 +00:00
# include "string_func.h"
# include "settings_type.h"
2009-01-04 15:32:25 +00:00
# include "gamelog_internal.h"
2008-06-03 18:35:58 +00:00
# include "console_func.h"
# include "debug.h"
2010-07-19 17:24:33 +00:00
# include "date_func.h"
2008-06-03 18:35:58 +00:00
# include "rev.h"
2009-01-04 15:32:25 +00:00
# include <stdarg.h>
2014-04-23 20:13:33 +00:00
# include "safeguards.h"
2019-01-29 00:56:28 +00:00
extern const SaveLoadVersion SAVEGAME_VERSION ; ///< current savegame version
2008-06-03 18:35:58 +00:00
extern SavegameType _savegame_type ; ///< type of savegame we are loading
2019-01-29 00:56:28 +00:00
extern uint32 _ttdp_version ; ///< version of TTDP savegame (if applicable)
extern SaveLoadVersion _sl_version ; ///< the major savegame version identifier
extern byte _sl_minor_version ; ///< the minor savegame version, DO NOT USE!
2008-06-03 18:35:58 +00:00
static GamelogActionType _gamelog_action_type = GLAT_NONE ; ///< action to record if anything changes
2019-04-10 21:07:06 +00:00
LoggedAction * _gamelog_action = nullptr ; ///< first logged action
2009-01-04 15:32:25 +00:00
uint _gamelog_actions = 0 ; ///< number of actions
2019-04-10 21:07:06 +00:00
static LoggedAction * _current_action = nullptr ; ///< current action we are logging, nullptr when there is no action active
2008-06-03 18:35:58 +00:00
2019-01-27 15:13:28 +00:00
/**
* Return the revision string for the current client version , for use in gamelog .
* The string returned is at most GAMELOG_REVISION_LENGTH bytes long .
*/
static const char * GetGamelogRevisionString ( )
{
/* Allocate a buffer larger than necessary (git revision hash is 40 bytes) to avoid truncation later */
static char gamelog_revision [ 48 ] = { 0 } ;
2020-12-27 10:44:22 +00:00
static_assert ( lengthof ( gamelog_revision ) > GAMELOG_REVISION_LENGTH ) ;
2019-01-27 15:13:28 +00:00
if ( IsReleasedVersion ( ) ) {
return _openttd_revision ;
} else if ( gamelog_revision [ 0 ] = = 0 ) {
/* Prefix character indication revision status */
assert ( _openttd_revision_modified < 3 ) ;
gamelog_revision [ 0 ] = " gum " [ _openttd_revision_modified ] ; // g = "git", u = "unknown", m = "modified"
/* Append the revision hash */
strecat ( gamelog_revision , _openttd_revision_hash , lastof ( gamelog_revision ) ) ;
/* Truncate string to GAMELOG_REVISION_LENGTH bytes */
gamelog_revision [ GAMELOG_REVISION_LENGTH - 1 ] = ' \0 ' ;
}
return gamelog_revision ;
}
2010-08-01 19:22:34 +00:00
/**
* Stores information about new action , but doesn ' t allocate it
2008-06-03 18:35:58 +00:00
* Action is allocated only when there is at least one change
* @ param at type of action
*/
void GamelogStartAction ( GamelogActionType at )
{
assert ( _gamelog_action_type = = GLAT_NONE ) ; // do not allow starting new action without stopping the previous first
_gamelog_action_type = at ;
}
2010-08-01 19:22:34 +00:00
/**
* Stops logging of any changes
2008-06-03 18:35:58 +00:00
*/
void GamelogStopAction ( )
{
assert ( _gamelog_action_type ! = GLAT_NONE ) ; // nobody should try to stop if there is no action in progress
2019-04-10 21:07:06 +00:00
bool print = _current_action ! = nullptr ;
2008-07-18 12:11:46 +00:00
2019-04-10 21:07:06 +00:00
_current_action = nullptr ;
2008-06-03 18:35:58 +00:00
_gamelog_action_type = GLAT_NONE ;
2008-07-18 12:11:46 +00:00
if ( print ) GamelogPrintDebug ( 5 ) ;
2008-06-03 18:35:58 +00:00
}
2020-05-10 14:04:04 +00:00
void GamelogStopAnyAction ( )
{
if ( _gamelog_action_type ! = GLAT_NONE ) GamelogStopAction ( ) ;
}
2010-08-01 19:22:34 +00:00
/**
2012-02-05 15:51:13 +00:00
* Frees the memory allocated by a gamelog
2008-06-03 18:35:58 +00:00
*/
2012-02-05 15:51:13 +00:00
void GamelogFree ( LoggedAction * gamelog_action , uint gamelog_actions )
2008-06-03 18:35:58 +00:00
{
2012-02-05 15:51:13 +00:00
for ( uint i = 0 ; i < gamelog_actions ; i + + ) {
const LoggedAction * la = & gamelog_action [ i ] ;
2008-06-03 18:35:58 +00:00
for ( uint j = 0 ; j < la - > changes ; j + + ) {
const LoggedChange * lc = & la - > change [ j ] ;
2009-02-08 12:25:13 +00:00
if ( lc - > ct = = GLCT_SETTING ) free ( lc - > setting . name ) ;
2008-06-03 18:35:58 +00:00
}
free ( la - > change ) ;
}
2012-02-05 15:51:13 +00:00
free ( gamelog_action ) ;
}
/**
* Resets and frees all memory allocated - used before loading or starting a new game
*/
void GamelogReset ( )
{
assert ( _gamelog_action_type = = GLAT_NONE ) ;
GamelogFree ( _gamelog_action , _gamelog_actions ) ;
2008-06-03 18:35:58 +00:00
2019-04-10 21:07:06 +00:00
_gamelog_action = nullptr ;
2008-06-03 18:35:58 +00:00
_gamelog_actions = 0 ;
2019-04-10 21:07:06 +00:00
_current_action = nullptr ;
2008-06-03 18:35:58 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Prints GRF ID , checksum and filename if found
2014-04-25 15:25:59 +00:00
* @ param buf The location in the buffer to draw
* @ param last The end of the buffer
2008-06-03 18:35:58 +00:00
* @ param grfid GRF ID
2010-10-16 21:20:46 +00:00
* @ param md5sum array of md5sum to print , if known
* @ param gc GrfConfig , if known
2014-04-25 15:25:59 +00:00
* @ return The buffer location .
2008-06-03 18:35:58 +00:00
*/
2014-04-25 15:25:59 +00:00
static char * PrintGrfInfo ( char * buf , const char * last , uint grfid , const uint8 * md5sum , const GRFConfig * gc )
2008-06-03 18:35:58 +00:00
{
char txt [ 40 ] ;
2019-04-10 21:07:06 +00:00
if ( md5sum ! = nullptr ) {
2010-10-16 21:20:46 +00:00
md5sumToString ( txt , lastof ( txt ) , md5sum ) ;
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , last , " GRF ID %08X, checksum %s " , BSWAP32 ( grfid ) , txt ) ;
2010-10-16 21:20:46 +00:00
} else {
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , last , " GRF ID %08X " , BSWAP32 ( grfid ) ) ;
2010-10-16 21:20:46 +00:00
}
2008-06-03 18:35:58 +00:00
2019-04-10 21:07:06 +00:00
if ( gc ! = nullptr ) {
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , last , " , filename: %s (md5sum matches) " , gc - > filename ) ;
2010-10-16 21:20:46 +00:00
} else {
2010-10-17 12:12:13 +00:00
gc = FindGRFConfig ( grfid , FGCM_ANY ) ;
2019-04-10 21:07:06 +00:00
if ( gc ! = nullptr ) {
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , last , " , filename: %s (matches GRFID only) " , gc - > filename ) ;
2010-10-16 21:20:46 +00:00
} else {
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , last , " , unknown GRF " ) ;
2010-10-16 21:20:46 +00:00
}
}
2014-04-25 15:25:59 +00:00
return buf ;
2008-06-03 18:35:58 +00:00
}
/** Text messages for various logged actions */
2009-09-20 23:11:01 +00:00
static const char * const la_text [ ] = {
2008-06-03 18:35:58 +00:00
" new game started " ,
" game loaded " ,
" GRF config changed " ,
" cheat was used " ,
2009-02-08 12:25:13 +00:00
" settings changed " ,
2008-07-24 15:19:26 +00:00
" GRF bug triggered " ,
2009-03-30 00:21:43 +00:00
" emergency savegame " ,
2008-06-03 18:35:58 +00:00
} ;
2020-12-27 10:44:22 +00:00
static_assert ( lengthof ( la_text ) = = GLAT_END ) ;
2008-06-03 18:35:58 +00:00
2010-10-16 22:46:53 +00:00
/**
* Information about the presence of a Grf at a certain point during gamelog history
* Note about missing Grfs :
* Changes to missing Grfs are not logged including manual removal of the Grf .
* So if the gamelog tells a Grf is missing we do not know whether it was readded or completely removed
* at some later point .
*/
struct GRFPresence {
const GRFConfig * gc ; ///< GRFConfig, if known
bool was_missing ; ///< Grf was missing during some gameload in the past
GRFPresence ( const GRFConfig * gc ) : gc ( gc ) , was_missing ( false ) { }
2018-09-20 21:41:43 +00:00
GRFPresence ( ) = default ;
2010-10-16 22:46:53 +00:00
} ;
typedef SmallMap < uint32 , GRFPresence > GrfIDMapping ;
2008-06-03 18:35:58 +00:00
2009-09-19 09:51:14 +00:00
/**
* Prints active gamelog
* @ param proc the procedure to draw with
*/
2023-01-27 21:40:30 +00:00
void GamelogPrint ( std : : function < void ( const char * ) > proc )
2008-06-03 18:35:58 +00:00
{
2014-04-25 15:25:59 +00:00
char buffer [ 1024 ] ;
2010-10-16 21:13:55 +00:00
GrfIDMapping grf_names ;
2008-06-03 18:35:58 +00:00
proc ( " ---- gamelog start ---- " ) ;
const LoggedAction * laend = & _gamelog_action [ _gamelog_actions ] ;
for ( const LoggedAction * la = _gamelog_action ; la ! = laend ; la + + ) {
assert ( ( uint ) la - > at < GLAT_END ) ;
2014-04-25 15:25:59 +00:00
seprintf ( buffer , lastof ( buffer ) , " Tick %u: %s " , ( uint ) la - > tick , la_text [ ( uint ) la - > at ] ) ;
proc ( buffer ) ;
2008-06-03 18:35:58 +00:00
const LoggedChange * lcend = & la - > change [ la - > changes ] ;
for ( const LoggedChange * lc = la - > change ; lc ! = lcend ; lc + + ) {
2014-04-25 15:25:59 +00:00
char * buf = buffer ;
2008-06-03 18:35:58 +00:00
switch ( lc - > ct ) {
default : NOT_REACHED ( ) ;
case GLCT_MODE :
2023-02-25 15:59:43 +00:00
/* Changing landscape, or going from scenario editor to game or back. */
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " New game mode: %u landscape: %u " ,
2008-06-03 18:35:58 +00:00
( uint ) lc - > mode . mode , ( uint ) lc - > mode . landscape ) ;
break ;
case GLCT_REVISION :
2023-02-25 15:59:43 +00:00
/* The game was loaded in a diffferent version than before. */
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " Revision text changed to %s, savegame version %u, " ,
2008-06-03 18:35:58 +00:00
lc - > revision . text , lc - > revision . slver ) ;
switch ( lc - > revision . modified ) {
2014-04-25 15:25:59 +00:00
case 0 : buf + = seprintf ( buf , lastof ( buffer ) , " not " ) ; break ;
case 1 : buf + = seprintf ( buf , lastof ( buffer ) , " maybe " ) ; break ;
2008-06-03 18:35:58 +00:00
default : break ;
}
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " modified, _openttd_newgrf_version = 0x%08x " , lc - > revision . newgrf ) ;
2008-06-03 18:35:58 +00:00
break ;
case GLCT_OLDVER :
2023-02-25 15:59:43 +00:00
/* The game was loaded from before 0.7.0-beta1. */
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " Conversion from " ) ;
2008-06-03 18:35:58 +00:00
switch ( lc - > oldver . type ) {
default : NOT_REACHED ( ) ;
case SGT_OTTD :
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " OTTD savegame without gamelog: version %u, %u " ,
2008-06-03 18:35:58 +00:00
GB ( lc - > oldver . version , 8 , 16 ) , GB ( lc - > oldver . version , 0 , 8 ) ) ;
break ;
2009-01-23 02:35:17 +00:00
case SGT_TTO :
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " TTO savegame " ) ;
2009-01-23 02:35:17 +00:00
break ;
2008-06-03 18:35:58 +00:00
case SGT_TTD :
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " TTD savegame " ) ;
2008-06-03 18:35:58 +00:00
break ;
case SGT_TTDP1 :
case SGT_TTDP2 :
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " TTDP savegame, %s format " ,
2008-06-03 18:35:58 +00:00
lc - > oldver . type = = SGT_TTDP1 ? " old " : " new " ) ;
if ( lc - > oldver . version ! = 0 ) {
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " , TTDP version %u.%u.%u.%u " ,
2008-06-03 18:35:58 +00:00
GB ( lc - > oldver . version , 24 , 8 ) , GB ( lc - > oldver . version , 20 , 4 ) ,
GB ( lc - > oldver . version , 16 , 4 ) , GB ( lc - > oldver . version , 0 , 16 ) ) ;
}
break ;
}
break ;
2009-02-08 12:25:13 +00:00
case GLCT_SETTING :
2023-02-25 15:59:43 +00:00
/* A setting with the SF_NO_NETWORK flag got changed; these settings usually affect NewGRFs, such as road side or wagon speed limits. */
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " Setting changed: %s : %d -> %d " , lc - > setting . name , lc - > setting . oldval , lc - > setting . newval ) ;
2008-06-03 18:35:58 +00:00
break ;
2010-10-16 21:13:55 +00:00
case GLCT_GRFADD : {
2023-02-25 15:59:43 +00:00
/* A NewGRF got added to the game, either at the start of the game (never an issue), or later on when it could be an issue. */
2010-10-17 12:12:13 +00:00
const GRFConfig * gc = FindGRFConfig ( lc - > grfadd . grfid , FGCM_EXACT , lc - > grfadd . md5sum ) ;
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " Added NewGRF: " ) ;
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc - > grfadd . grfid , lc - > grfadd . md5sum , gc ) ;
2010-10-16 22:46:53 +00:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc - > grfrem . grfid ) ;
2014-04-25 15:25:59 +00:00
if ( gm ! = grf_names . End ( ) & & ! gm - > second . was_missing ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was already added! " ) ;
2010-10-16 21:13:55 +00:00
grf_names [ lc - > grfadd . grfid ] = gc ;
2008-06-03 18:35:58 +00:00
break ;
2010-10-16 21:13:55 +00:00
}
2008-06-03 18:35:58 +00:00
2010-10-16 21:20:46 +00:00
case GLCT_GRFREM : {
2023-02-25 15:59:43 +00:00
/* A NewGRF got removed from the game, either manually or by it missing when loading the game. */
2010-10-16 21:20:46 +00:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc - > grfrem . grfid ) ;
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , la - > at = = GLAT_LOAD ? " Missing NewGRF: " : " Removed NewGRF: " ) ;
2019-04-10 21:07:06 +00:00
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc - > grfrem . grfid , nullptr , gm ! = grf_names . End ( ) ? gm - > second . gc : nullptr ) ;
2010-10-16 22:46:53 +00:00
if ( gm = = grf_names . End ( ) ) {
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2010-10-16 22:46:53 +00:00
} else {
if ( la - > at = = GLAT_LOAD ) {
/* Missing grfs on load are not removed from the configuration */
gm - > second . was_missing = true ;
} else {
grf_names . Erase ( gm ) ;
}
}
2008-06-03 18:35:58 +00:00
break ;
2010-10-16 21:20:46 +00:00
}
2008-06-03 18:35:58 +00:00
2010-10-16 21:13:55 +00:00
case GLCT_GRFCOMPAT : {
2023-02-25 15:59:43 +00:00
/* Another version of the same NewGRF got loaded. */
2010-10-17 12:12:13 +00:00
const GRFConfig * gc = FindGRFConfig ( lc - > grfadd . grfid , FGCM_EXACT , lc - > grfadd . md5sum ) ;
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " Compatible NewGRF loaded: " ) ;
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc - > grfcompat . grfid , lc - > grfcompat . md5sum , gc ) ;
if ( ! grf_names . Contains ( lc - > grfcompat . grfid ) ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2010-10-16 21:13:55 +00:00
grf_names [ lc - > grfcompat . grfid ] = gc ;
2008-06-03 18:35:58 +00:00
break ;
2010-10-16 21:13:55 +00:00
}
2008-06-03 18:35:58 +00:00
2010-10-16 21:20:46 +00:00
case GLCT_GRFPARAM : {
2023-02-25 15:59:43 +00:00
/* A parameter of a NewGRF got changed after the game was started. */
2010-10-16 21:20:46 +00:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc - > grfrem . grfid ) ;
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " GRF parameter changed: " ) ;
2019-04-10 21:07:06 +00:00
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc - > grfparam . grfid , nullptr , gm ! = grf_names . End ( ) ? gm - > second . gc : nullptr ) ;
2014-04-25 15:25:59 +00:00
if ( gm = = grf_names . End ( ) ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2008-06-03 18:35:58 +00:00
break ;
2010-10-16 21:20:46 +00:00
}
2008-06-03 18:35:58 +00:00
2010-10-16 21:20:46 +00:00
case GLCT_GRFMOVE : {
2023-02-25 15:59:43 +00:00
/* The order of NewGRFs got changed, which might cause some other NewGRFs to behave differently. */
2010-10-16 21:20:46 +00:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc - > grfrem . grfid ) ;
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " GRF order changed: %08X moved %d places %s " ,
2008-06-03 18:35:58 +00:00
BSWAP32 ( lc - > grfmove . grfid ) , abs ( lc - > grfmove . offset ) , lc - > grfmove . offset > = 0 ? " down " : " up " ) ;
2019-04-10 21:07:06 +00:00
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc - > grfmove . grfid , nullptr , gm ! = grf_names . End ( ) ? gm - > second . gc : nullptr ) ;
2014-04-25 15:25:59 +00:00
if ( gm = = grf_names . End ( ) ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2008-06-03 18:35:58 +00:00
break ;
2010-10-16 21:20:46 +00:00
}
2008-07-24 15:19:26 +00:00
2010-10-16 21:20:46 +00:00
case GLCT_GRFBUG : {
2023-02-25 15:59:43 +00:00
/* A specific bug in a NewGRF, that could cause wide spread problems, has been noted during the execution of the game. */
2010-10-16 21:20:46 +00:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc - > grfrem . grfid ) ;
2008-07-24 15:19:26 +00:00
switch ( lc - > grfbug . bug ) {
default : NOT_REACHED ( ) ;
case GBUG_VEH_LENGTH :
2014-04-25 15:25:59 +00:00
buf + = seprintf ( buf , lastof ( buffer ) , " Rail vehicle changes length outside a depot: GRF ID %08X, internal ID 0x%X " , BSWAP32 ( lc - > grfbug . grfid ) , ( uint ) lc - > grfbug . data ) ;
2008-07-24 15:19:26 +00:00
break ;
}
2019-04-10 21:07:06 +00:00
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc - > grfbug . grfid , nullptr , gm ! = grf_names . End ( ) ? gm - > second . gc : nullptr ) ;
2014-04-25 15:25:59 +00:00
if ( gm = = grf_names . End ( ) ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2010-10-16 21:13:55 +00:00
break ;
2010-10-16 21:20:46 +00:00
}
2009-03-30 00:21:43 +00:00
case GLCT_EMERGENCY :
2023-02-25 15:59:43 +00:00
/* At one point the savegame was made during the handling of a game crash.
* The generic code already mentioned the emergency savegame , and there is no extra information to log . */
2009-03-30 00:21:43 +00:00
break ;
2008-06-03 18:35:58 +00:00
}
2014-04-25 15:25:59 +00:00
proc ( buffer ) ;
2008-06-03 18:35:58 +00:00
}
}
proc ( " ---- gamelog end ---- " ) ;
}
2011-05-02 17:42:12 +00:00
/** Print the gamelog data to the console. */
2008-06-03 18:35:58 +00:00
void GamelogPrintConsole ( )
{
2023-01-27 21:40:30 +00:00
GamelogPrint ( [ ] ( const char * s ) {
IConsolePrint ( CC_WARNING , s ) ;
} ) ;
2008-06-03 18:35:58 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Prints gamelog to debug output . Code is executed even when
2008-07-18 12:11:46 +00:00
* there will be no output . It is called very seldom , so it
* doesn ' t matter that much . At least it gives more uniform code . . .
* @ param level debug level we need to print stuff
*/
void GamelogPrintDebug ( int level )
2008-06-03 18:35:58 +00:00
{
2023-01-27 21:40:30 +00:00
GamelogPrint ( [ level ] ( const char * s ) {
Debug ( gamelog , level , " {} " , s ) ;
} ) ;
2008-06-03 18:35:58 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Allocates new LoggedChange and new LoggedAction if needed .
2019-04-10 21:07:06 +00:00
* If there is no action active , nullptr is returned .
2008-06-03 18:35:58 +00:00
* @ param ct type of change
2019-04-10 21:07:06 +00:00
* @ return new LoggedChange , or nullptr if there is no action active
2008-06-03 18:35:58 +00:00
*/
static LoggedChange * GamelogChange ( GamelogChangeType ct )
{
2019-04-10 21:07:06 +00:00
if ( _current_action = = nullptr ) {
if ( _gamelog_action_type = = GLAT_NONE ) return nullptr ;
2008-06-03 18:35:58 +00:00
_gamelog_action = ReallocT ( _gamelog_action , _gamelog_actions + 1 ) ;
_current_action = & _gamelog_action [ _gamelog_actions + + ] ;
_current_action - > at = _gamelog_action_type ;
_current_action - > tick = _tick_counter ;
2019-04-10 21:07:06 +00:00
_current_action - > change = nullptr ;
2008-06-03 18:35:58 +00:00
_current_action - > changes = 0 ;
}
_current_action - > change = ReallocT ( _current_action - > change , _current_action - > changes + 1 ) ;
LoggedChange * lc = & _current_action - > change [ _current_action - > changes + + ] ;
lc - > ct = ct ;
return lc ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs a emergency savegame
2009-03-30 00:21:43 +00:00
*/
void GamelogEmergency ( )
{
2009-09-14 19:30:13 +00:00
/* Terminate any active action */
if ( _gamelog_action_type ! = GLAT_NONE ) GamelogStopAction ( ) ;
GamelogStartAction ( GLAT_EMERGENCY ) ;
2009-03-30 00:21:43 +00:00
GamelogChange ( GLCT_EMERGENCY ) ;
2009-09-14 19:30:13 +00:00
GamelogStopAction ( ) ;
2009-03-30 00:21:43 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Finds out if current game is a loaded emergency savegame .
2009-03-30 00:21:43 +00:00
*/
bool GamelogTestEmergency ( )
{
2019-04-10 21:07:06 +00:00
const LoggedChange * emergency = nullptr ;
2009-03-30 00:21:43 +00:00
const LoggedAction * laend = & _gamelog_action [ _gamelog_actions ] ;
for ( const LoggedAction * la = _gamelog_action ; la ! = laend ; la + + ) {
const LoggedChange * lcend = & la - > change [ la - > changes ] ;
for ( const LoggedChange * lc = la - > change ; lc ! = lcend ; lc + + ) {
if ( lc - > ct = = GLCT_EMERGENCY ) emergency = lc ;
}
}
2019-04-10 21:07:06 +00:00
return ( emergency ! = nullptr ) ;
2009-03-30 00:21:43 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Logs a change in game revision
2008-06-03 18:35:58 +00:00
*/
void GamelogRevision ( )
{
assert ( _gamelog_action_type = = GLAT_START | | _gamelog_action_type = = GLAT_LOAD ) ;
LoggedChange * lc = GamelogChange ( GLCT_REVISION ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
2009-01-27 14:31:33 +00:00
memset ( lc - > revision . text , 0 , sizeof ( lc - > revision . text ) ) ;
2019-01-27 15:13:28 +00:00
strecpy ( lc - > revision . text , GetGamelogRevisionString ( ) , lastof ( lc - > revision . text ) ) ;
2008-06-03 18:35:58 +00:00
lc - > revision . slver = SAVEGAME_VERSION ;
lc - > revision . modified = _openttd_revision_modified ;
lc - > revision . newgrf = _openttd_newgrf_version ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs a change in game mode ( scenario editor or game )
2008-06-03 18:35:58 +00:00
*/
void GamelogMode ( )
{
assert ( _gamelog_action_type = = GLAT_START | | _gamelog_action_type = = GLAT_LOAD | | _gamelog_action_type = = GLAT_CHEAT ) ;
LoggedChange * lc = GamelogChange ( GLCT_MODE ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
lc - > mode . mode = _game_mode ;
lc - > mode . landscape = _settings_game . game_creation . landscape ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs loading from savegame without gamelog
2008-06-03 18:35:58 +00:00
*/
void GamelogOldver ( )
{
assert ( _gamelog_action_type = = GLAT_LOAD ) ;
LoggedChange * lc = GamelogChange ( GLCT_OLDVER ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
lc - > oldver . type = _savegame_type ;
lc - > oldver . version = ( _savegame_type = = SGT_OTTD ? ( ( uint32 ) _sl_version < < 8 | _sl_minor_version ) : _ttdp_version ) ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs change in game settings . Only non - networksafe settings are logged
2009-02-08 12:25:13 +00:00
* @ param name setting name
* @ param oldval old setting value
* @ param newval new setting value
2008-06-03 18:35:58 +00:00
*/
2021-05-30 09:51:21 +00:00
void GamelogSetting ( const std : : string & name , int32 oldval , int32 newval )
2008-06-03 18:35:58 +00:00
{
2009-02-08 12:25:13 +00:00
assert ( _gamelog_action_type = = GLAT_SETTING ) ;
2008-06-03 18:35:58 +00:00
2009-02-08 12:25:13 +00:00
LoggedChange * lc = GamelogChange ( GLCT_SETTING ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
2021-05-30 09:51:21 +00:00
lc - > setting . name = stredup ( name . c_str ( ) ) ;
2009-02-08 12:25:13 +00:00
lc - > setting . oldval = oldval ;
lc - > setting . newval = newval ;
2008-06-03 18:35:58 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Finds out if current revision is different than last revision stored in the savegame .
2008-06-03 18:35:58 +00:00
* Appends GLCT_REVISION when the revision string changed
*/
void GamelogTestRevision ( )
{
2019-04-10 21:07:06 +00:00
const LoggedChange * rev = nullptr ;
2008-06-03 18:35:58 +00:00
const LoggedAction * laend = & _gamelog_action [ _gamelog_actions ] ;
for ( const LoggedAction * la = _gamelog_action ; la ! = laend ; la + + ) {
const LoggedChange * lcend = & la - > change [ la - > changes ] ;
for ( const LoggedChange * lc = la - > change ; lc ! = lcend ; lc + + ) {
if ( lc - > ct = = GLCT_REVISION ) rev = lc ;
}
}
2019-04-10 21:07:06 +00:00
if ( rev = = nullptr | | strcmp ( rev - > revision . text , GetGamelogRevisionString ( ) ) ! = 0 | |
2008-06-03 18:35:58 +00:00
rev - > revision . modified ! = _openttd_revision_modified | |
rev - > revision . newgrf ! = _openttd_newgrf_version ) {
GamelogRevision ( ) ;
}
}
2010-08-01 19:22:34 +00:00
/**
* Finds last stored game mode or landscape .
2008-06-03 18:35:58 +00:00
* Any change is logged
*/
void GamelogTestMode ( )
{
2019-04-10 21:07:06 +00:00
const LoggedChange * mode = nullptr ;
2008-06-03 18:35:58 +00:00
const LoggedAction * laend = & _gamelog_action [ _gamelog_actions ] ;
for ( const LoggedAction * la = _gamelog_action ; la ! = laend ; la + + ) {
const LoggedChange * lcend = & la - > change [ la - > changes ] ;
for ( const LoggedChange * lc = la - > change ; lc ! = lcend ; lc + + ) {
if ( lc - > ct = = GLCT_MODE ) mode = lc ;
}
}
2019-04-10 21:07:06 +00:00
if ( mode = = nullptr | | mode - > mode . mode ! = _game_mode | | mode - > mode . landscape ! = _settings_game . game_creation . landscape ) GamelogMode ( ) ;
2008-06-03 18:35:58 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Logs triggered GRF bug .
2008-07-24 15:19:26 +00:00
* @ param grfid ID of problematic GRF
* @ param bug type of bug , @ see enum GRFBugs
* @ param data additional data
*/
static void GamelogGRFBug ( uint32 grfid , byte bug , uint64 data )
{
assert ( _gamelog_action_type = = GLAT_GRFBUG ) ;
LoggedChange * lc = GamelogChange ( GLCT_GRFBUG ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-07-24 15:19:26 +00:00
lc - > grfbug . data = data ;
lc - > grfbug . grfid = grfid ;
lc - > grfbug . bug = bug ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs GRF bug - rail vehicle has different length after reversing .
2008-07-24 15:19:26 +00:00
* Ensures this is logged only once for each GRF and engine type
* This check takes some time , but it is called pretty seldom , so it
* doesn ' t matter that much ( ideally it shouldn ' t be called at all ) .
2009-09-19 09:51:14 +00:00
* @ param grfid the broken NewGRF
* @ param internal_id the internal ID of whatever ' s broken in the NewGRF
2008-07-24 15:19:26 +00:00
* @ return true iff a unique record was done
*/
bool GamelogGRFBugReverse ( uint32 grfid , uint16 internal_id )
{
const LoggedAction * laend = & _gamelog_action [ _gamelog_actions ] ;
for ( const LoggedAction * la = _gamelog_action ; la ! = laend ; la + + ) {
const LoggedChange * lcend = & la - > change [ la - > changes ] ;
for ( const LoggedChange * lc = la - > change ; lc ! = lcend ; lc + + ) {
if ( lc - > ct = = GLCT_GRFBUG & & lc - > grfbug . grfid = = grfid & &
lc - > grfbug . bug = = GBUG_VEH_LENGTH & & lc - > grfbug . data = = internal_id ) {
return false ;
}
}
}
GamelogStartAction ( GLAT_GRFBUG ) ;
GamelogGRFBug ( grfid , GBUG_VEH_LENGTH , internal_id ) ;
GamelogStopAction ( ) ;
return true ;
}
2010-08-01 19:22:34 +00:00
/**
* Decides if GRF should be logged
2008-06-03 18:35:58 +00:00
* @ param g grf to determine
* @ return true iff GRF is not static and is loaded
*/
static inline bool IsLoggableGrfConfig ( const GRFConfig * g )
{
return ! HasBit ( g - > flags , GCF_STATIC ) & & g - > status ! = GCS_NOT_FOUND ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs removal of a GRF
2008-06-03 18:35:58 +00:00
* @ param grfid ID of removed GRF
*/
void GamelogGRFRemove ( uint32 grfid )
{
assert ( _gamelog_action_type = = GLAT_LOAD | | _gamelog_action_type = = GLAT_GRF ) ;
LoggedChange * lc = GamelogChange ( GLCT_GRFREM ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
lc - > grfrem . grfid = grfid ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs adding of a GRF
2008-06-03 18:35:58 +00:00
* @ param newg added GRF
*/
void GamelogGRFAdd ( const GRFConfig * newg )
{
assert ( _gamelog_action_type = = GLAT_LOAD | | _gamelog_action_type = = GLAT_START | | _gamelog_action_type = = GLAT_GRF ) ;
if ( ! IsLoggableGrfConfig ( newg ) ) return ;
LoggedChange * lc = GamelogChange ( GLCT_GRFADD ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
2010-03-12 19:18:26 +00:00
lc - > grfadd = newg - > ident ;
2008-06-03 18:35:58 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Logs loading compatible GRF
2008-06-03 18:35:58 +00:00
* ( the same ID , but different MD5 hash )
* @ param newg new ( updated ) GRF
*/
void GamelogGRFCompatible ( const GRFIdentifier * newg )
{
2008-06-06 19:27:09 +00:00
assert ( _gamelog_action_type = = GLAT_LOAD | | _gamelog_action_type = = GLAT_GRF ) ;
2008-06-03 18:35:58 +00:00
LoggedChange * lc = GamelogChange ( GLCT_GRFCOMPAT ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
2010-03-12 19:18:26 +00:00
lc - > grfcompat = * newg ;
2008-06-03 18:35:58 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Logs changing GRF order
2008-06-03 18:35:58 +00:00
* @ param grfid GRF that is moved
* @ param offset how far it is moved , positive = moved down
*/
static void GamelogGRFMove ( uint32 grfid , int32 offset )
{
assert ( _gamelog_action_type = = GLAT_GRF ) ;
LoggedChange * lc = GamelogChange ( GLCT_GRFMOVE ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
lc - > grfmove . grfid = grfid ;
lc - > grfmove . offset = offset ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs change in GRF parameters .
2008-06-03 18:35:58 +00:00
* Details about parameters changed are not stored
* @ param grfid ID of GRF to store
*/
static void GamelogGRFParameters ( uint32 grfid )
{
assert ( _gamelog_action_type = = GLAT_GRF ) ;
LoggedChange * lc = GamelogChange ( GLCT_GRFPARAM ) ;
2019-04-10 21:07:06 +00:00
if ( lc = = nullptr ) return ;
2008-06-03 18:35:58 +00:00
lc - > grfparam . grfid = grfid ;
}
2010-08-01 19:22:34 +00:00
/**
* Logs adding of list of GRFs .
2008-06-03 18:35:58 +00:00
* Useful when old savegame is loaded or when new game is started
* @ param newg head of GRF linked list
*/
void GamelogGRFAddList ( const GRFConfig * newg )
{
assert ( _gamelog_action_type = = GLAT_START | | _gamelog_action_type = = GLAT_LOAD ) ;
2019-04-10 21:07:06 +00:00
for ( ; newg ! = nullptr ; newg = newg - > next ) {
2008-06-03 18:35:58 +00:00
GamelogGRFAdd ( newg ) ;
}
}
/** List of GRFs using array of pointers instead of linked list */
struct GRFList {
uint n ;
2009-06-01 11:49:46 +00:00
const GRFConfig * grf [ ] ;
2008-06-03 18:35:58 +00:00
} ;
2010-08-01 19:22:34 +00:00
/**
* Generates GRFList
2008-06-03 18:35:58 +00:00
* @ param grfc head of GRF linked list
*/
static GRFList * GenerateGRFList ( const GRFConfig * grfc )
{
uint n = 0 ;
2019-04-10 21:07:06 +00:00
for ( const GRFConfig * g = grfc ; g ! = nullptr ; g = g - > next ) {
2008-06-03 18:35:58 +00:00
if ( IsLoggableGrfConfig ( g ) ) n + + ;
}
GRFList * list = ( GRFList * ) MallocT < byte > ( sizeof ( GRFList ) + n * sizeof ( GRFConfig * ) ) ;
list - > n = 0 ;
2019-04-10 21:07:06 +00:00
for ( const GRFConfig * g = grfc ; g ! = nullptr ; g = g - > next ) {
2008-06-03 18:35:58 +00:00
if ( IsLoggableGrfConfig ( g ) ) list - > grf [ list - > n + + ] = g ;
}
return list ;
}
2010-08-01 19:22:34 +00:00
/**
* Compares two NewGRF lists and logs any change
2008-06-03 18:35:58 +00:00
* @ param oldc original GRF list
* @ param newc new GRF list
*/
void GamelogGRFUpdate ( const GRFConfig * oldc , const GRFConfig * newc )
{
GRFList * ol = GenerateGRFList ( oldc ) ;
GRFList * nl = GenerateGRFList ( newc ) ;
uint o = 0 , n = 0 ;
while ( o < ol - > n & & n < nl - > n ) {
const GRFConfig * og = ol - > grf [ o ] ;
const GRFConfig * ng = nl - > grf [ n ] ;
2010-02-25 20:05:31 +00:00
if ( og - > ident . grfid ! = ng - > ident . grfid ) {
2008-06-03 18:35:58 +00:00
uint oi , ni ;
for ( oi = 0 ; oi < ol - > n ; oi + + ) {
2010-02-25 20:05:31 +00:00
if ( ol - > grf [ oi ] - > ident . grfid = = nl - > grf [ n ] - > ident . grfid ) break ;
2008-06-03 18:35:58 +00:00
}
if ( oi < o ) {
/* GRF was moved, this change has been logged already */
n + + ;
continue ;
}
if ( oi = = ol - > n ) {
/* GRF couldn't be found in the OLD list, GRF was ADDED */
GamelogGRFAdd ( nl - > grf [ n + + ] ) ;
continue ;
}
for ( ni = 0 ; ni < nl - > n ; ni + + ) {
2010-02-25 20:05:31 +00:00
if ( nl - > grf [ ni ] - > ident . grfid = = ol - > grf [ o ] - > ident . grfid ) break ;
2008-06-03 18:35:58 +00:00
}
if ( ni < n ) {
/* GRF was moved, this change has been logged already */
o + + ;
continue ;
}
if ( ni = = nl - > n ) {
/* GRF couldn't be found in the NEW list, GRF was REMOVED */
2010-02-25 20:05:31 +00:00
GamelogGRFRemove ( ol - > grf [ o + + ] - > ident . grfid ) ;
2008-06-03 18:35:58 +00:00
continue ;
}
/* o < oi < ol->n
* n < ni < nl - > n */
assert ( ni > n & & ni < nl - > n ) ;
assert ( oi > o & & oi < ol - > n ) ;
ni - = n ; // number of GRFs it was moved downwards
oi - = o ; // number of GRFs it was moved upwards
if ( ni > = oi ) { // prefer the one that is moved further
/* GRF was moved down */
2010-02-25 20:05:31 +00:00
GamelogGRFMove ( ol - > grf [ o + + ] - > ident . grfid , ni ) ;
2008-06-03 18:35:58 +00:00
} else {
2010-02-25 20:05:31 +00:00
GamelogGRFMove ( nl - > grf [ n + + ] - > ident . grfid , - ( int ) oi ) ;
2008-06-03 18:35:58 +00:00
}
} else {
2010-02-25 20:05:31 +00:00
if ( memcmp ( og - > ident . md5sum , ng - > ident . md5sum , sizeof ( og - > ident . md5sum ) ) ! = 0 ) {
2008-06-03 18:35:58 +00:00
/* md5sum changed, probably loading 'compatible' GRF */
2010-02-25 20:05:31 +00:00
GamelogGRFCompatible ( & nl - > grf [ n ] - > ident ) ;
2008-06-03 18:35:58 +00:00
}
if ( og - > num_params ! = ng - > num_params | | memcmp ( og - > param , ng - > param , og - > num_params * sizeof ( og - > param [ 0 ] ) ) ! = 0 ) {
2010-02-25 20:05:31 +00:00
GamelogGRFParameters ( ol - > grf [ o ] - > ident . grfid ) ;
2008-06-03 18:35:58 +00:00
}
o + + ;
n + + ;
}
}
2010-02-25 20:05:31 +00:00
while ( o < ol - > n ) GamelogGRFRemove ( ol - > grf [ o + + ] - > ident . grfid ) ; // remaining GRFs were removed ...
while ( n < nl - > n ) GamelogGRFAdd ( nl - > grf [ n + + ] ) ; // ... or added
2008-06-03 18:35:58 +00:00
free ( ol ) ;
free ( nl ) ;
}
2011-10-30 13:47:45 +00:00
/**
* Get some basic information from the given gamelog .
* @ param gamelog_action Pointer to the gamelog to extract information from .
* @ param gamelog_actions Number of actions in the given gamelog .
2018-10-28 02:17:36 +00:00
* @ param [ out ] last_ottd_rev OpenTTD NewGRF version from the binary that saved the savegame last .
* @ param [ out ] ever_modified Max value of ' modified ' from all binaries that ever saved this savegame .
* @ param [ out ] removed_newgrfs Set to true if any NewGRFs have been removed .
2011-10-30 13:47:45 +00:00
*/
void GamelogInfo ( LoggedAction * gamelog_action , uint gamelog_actions , uint32 * last_ottd_rev , byte * ever_modified , bool * removed_newgrfs )
{
const LoggedAction * laend = & gamelog_action [ gamelog_actions ] ;
for ( const LoggedAction * la = gamelog_action ; la ! = laend ; la + + ) {
const LoggedChange * lcend = & la - > change [ la - > changes ] ;
for ( const LoggedChange * lc = la - > change ; lc ! = lcend ; lc + + ) {
switch ( lc - > ct ) {
default : break ;
case GLCT_REVISION :
* last_ottd_rev = lc - > revision . newgrf ;
2021-01-08 10:16:18 +00:00
* ever_modified = std : : max ( * ever_modified , lc - > revision . modified ) ;
2011-10-30 13:47:45 +00:00
break ;
case GLCT_GRFREM :
* removed_newgrfs = true ;
break ;
}
}
}
}