2009-08-21 22:21:05 +02: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 20:35:58 +02:00
/** @file gamelog.cpp Definition of functions used for logging of important changes in the game */
# include "stdafx.h"
2009-01-04 16:32:25 +01:00
# include "saveload/saveload.h"
2008-06-03 20:35:58 +02:00
# include "string_func.h"
# include "settings_type.h"
2009-01-04 16:32:25 +01:00
# include "gamelog_internal.h"
2008-06-03 20:35:58 +02:00
# include "console_func.h"
# include "debug.h"
2023-05-04 15:14:12 +02:00
# include "timer/timer_game_calendar.h"
2023-04-24 18:55:40 +02:00
# include "timer/timer_game_tick.h"
2008-06-03 20:35:58 +02:00
# include "rev.h"
2009-01-04 16:32:25 +01:00
# include <stdarg.h>
2014-04-23 22:13:33 +02:00
# include "safeguards.h"
2019-01-29 01:56:28 +01:00
extern const SaveLoadVersion SAVEGAME_VERSION ; ///< current savegame version
2008-06-03 20:35:58 +02:00
extern SavegameType _savegame_type ; ///< type of savegame we are loading
2019-01-29 01:56:28 +01: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 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
Gamelog _gamelog ; ///< Gamelog instance
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange : : ~ LoggedChange ( )
{
if ( this - > ct = = GLCT_SETTING ) free ( this - > setting . name ) ;
}
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
Gamelog : : Gamelog ( )
{
this - > data = std : : make_unique < GamelogInternalData > ( ) ;
this - > action_type = GLAT_NONE ;
this - > current_action = nullptr ;
}
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
Gamelog : : ~ Gamelog ( )
{
}
2008-06-03 20:35:58 +02:00
2019-01-27 16:13:28 +01: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 11:44:22 +01:00
static_assert ( lengthof ( gamelog_revision ) > GAMELOG_REVISION_LENGTH ) ;
2019-01-27 16:13:28 +01: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 21:22:34 +02:00
/**
* Stores information about new action , but doesn ' t allocate it
2008-06-03 20:35:58 +02:00
* Action is allocated only when there is at least one change
* @ param at type of action
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : StartAction ( GamelogActionType at )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_NONE ) ; // do not allow starting new action without stopping the previous first
this - > action_type = at ;
2008-06-03 20:35:58 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Stops logging of any changes
2008-06-03 20:35:58 +02:00
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : StopAction ( )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type ! = GLAT_NONE ) ; // nobody should try to stop if there is no action in progress
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
bool print = this - > current_action ! = nullptr ;
2008-07-18 14:11:46 +02:00
2023-05-01 19:14:31 +02:00
this - > current_action = nullptr ;
this - > action_type = GLAT_NONE ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
if ( print ) this - > PrintDebug ( 5 ) ;
2008-06-03 20:35:58 +02:00
}
2023-05-01 19:14:31 +02:00
void Gamelog : : StopAnyAction ( )
2020-05-10 16:04:04 +02:00
{
2023-05-01 19:14:31 +02:00
if ( this - > action_type ! = GLAT_NONE ) this - > StopAction ( ) ;
2012-02-05 16:51:13 +01:00
}
/**
* Resets and frees all memory allocated - used before loading or starting a new game
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : Reset ( )
2012-02-05 16:51:13 +01:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_NONE ) ;
this - > data - > action . clear ( ) ;
this - > current_action = nullptr ;
2008-06-03 20:35:58 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Prints GRF ID , checksum and filename if found
2014-04-25 17:25:59 +02:00
* @ param buf The location in the buffer to draw
* @ param last The end of the buffer
2008-06-03 20:35:58 +02:00
* @ param grfid GRF ID
2010-10-16 23:20:46 +02:00
* @ param md5sum array of md5sum to print , if known
* @ param gc GrfConfig , if known
2014-04-25 17:25:59 +02:00
* @ return The buffer location .
2008-06-03 20:35:58 +02:00
*/
2014-04-25 17:25:59 +02:00
static char * PrintGrfInfo ( char * buf , const char * last , uint grfid , const uint8 * md5sum , const GRFConfig * gc )
2008-06-03 20:35:58 +02:00
{
char txt [ 40 ] ;
2019-04-10 23:07:06 +02:00
if ( md5sum ! = nullptr ) {
2010-10-16 23:20:46 +02:00
md5sumToString ( txt , lastof ( txt ) , md5sum ) ;
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , last , " GRF ID %08X, checksum %s " , BSWAP32 ( grfid ) , txt ) ;
2010-10-16 23:20:46 +02:00
} else {
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , last , " GRF ID %08X " , BSWAP32 ( grfid ) ) ;
2010-10-16 23:20:46 +02:00
}
2008-06-03 20:35:58 +02:00
2019-04-10 23:07:06 +02:00
if ( gc ! = nullptr ) {
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , last , " , filename: %s (md5sum matches) " , gc - > filename ) ;
2010-10-16 23:20:46 +02:00
} else {
2010-10-17 14:12:13 +02:00
gc = FindGRFConfig ( grfid , FGCM_ANY ) ;
2019-04-10 23:07:06 +02:00
if ( gc ! = nullptr ) {
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , last , " , filename: %s (matches GRFID only) " , gc - > filename ) ;
2010-10-16 23:20:46 +02:00
} else {
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , last , " , unknown GRF " ) ;
2010-10-16 23:20:46 +02:00
}
}
2014-04-25 17:25:59 +02:00
return buf ;
2008-06-03 20:35:58 +02:00
}
/** Text messages for various logged actions */
2009-09-21 01:11:01 +02:00
static const char * const la_text [ ] = {
2008-06-03 20:35:58 +02:00
" new game started " ,
" game loaded " ,
" GRF config changed " ,
" cheat was used " ,
2009-02-08 13:25:13 +01:00
" settings changed " ,
2008-07-24 17:19:26 +02:00
" GRF bug triggered " ,
2009-03-30 02:21:43 +02:00
" emergency savegame " ,
2008-06-03 20:35:58 +02:00
} ;
2020-12-27 11:44:22 +01:00
static_assert ( lengthof ( la_text ) = = GLAT_END ) ;
2008-06-03 20:35:58 +02:00
2010-10-17 00:46:53 +02: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 23:41:43 +02:00
GRFPresence ( ) = default ;
2010-10-17 00:46:53 +02:00
} ;
typedef SmallMap < uint32 , GRFPresence > GrfIDMapping ;
2008-06-03 20:35:58 +02:00
2009-09-19 11:51:14 +02:00
/**
* Prints active gamelog
* @ param proc the procedure to draw with
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : Print ( std : : function < void ( const char * ) > proc )
2008-06-03 20:35:58 +02:00
{
2014-04-25 17:25:59 +02:00
char buffer [ 1024 ] ;
2010-10-16 23:13:55 +02:00
GrfIDMapping grf_names ;
2008-06-03 20:35:58 +02:00
proc ( " ---- gamelog start ---- " ) ;
2023-05-01 19:14:31 +02:00
for ( const LoggedAction & la : this - > data - > action ) {
assert ( ( uint ) la . at < GLAT_END ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
seprintf ( buffer , lastof ( buffer ) , " Tick %u: %s " , ( uint ) la . tick , la_text [ ( uint ) la . at ] ) ;
2014-04-25 17:25:59 +02:00
proc ( buffer ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
for ( const LoggedChange & lc : la . change ) {
2014-04-25 17:25:59 +02:00
char * buf = buffer ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
switch ( lc . ct ) {
2008-06-03 20:35:58 +02:00
default : NOT_REACHED ( ) ;
case GLCT_MODE :
2023-02-25 16:59:43 +01:00
/* Changing landscape, or going from scenario editor to game or back. */
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " New game mode: %u landscape: %u " ,
2023-05-01 19:14:31 +02:00
( uint ) lc . mode . mode , ( uint ) lc . mode . landscape ) ;
2008-06-03 20:35:58 +02:00
break ;
case GLCT_REVISION :
2023-02-25 16:59:43 +01:00
/* The game was loaded in a diffferent version than before. */
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " Revision text changed to %s, savegame version %u, " ,
2023-05-01 19:14:31 +02:00
lc . revision . text , lc . revision . slver ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
switch ( lc . revision . modified ) {
2014-04-25 17:25:59 +02:00
case 0 : buf + = seprintf ( buf , lastof ( buffer ) , " not " ) ; break ;
case 1 : buf + = seprintf ( buf , lastof ( buffer ) , " maybe " ) ; break ;
2008-06-03 20:35:58 +02:00
default : break ;
}
2023-05-01 19:14:31 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " modified, _openttd_newgrf_version = 0x%08x " , lc . revision . newgrf ) ;
2008-06-03 20:35:58 +02:00
break ;
case GLCT_OLDVER :
2023-02-25 16:59:43 +01:00
/* The game was loaded from before 0.7.0-beta1. */
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " Conversion from " ) ;
2023-05-01 19:14:31 +02:00
switch ( lc . oldver . type ) {
2008-06-03 20:35:58 +02:00
default : NOT_REACHED ( ) ;
case SGT_OTTD :
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " OTTD savegame without gamelog: version %u, %u " ,
2023-05-01 19:14:31 +02:00
GB ( lc . oldver . version , 8 , 16 ) , GB ( lc . oldver . version , 0 , 8 ) ) ;
2008-06-03 20:35:58 +02:00
break ;
2009-01-23 03:35:17 +01:00
case SGT_TTO :
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " TTO savegame " ) ;
2009-01-23 03:35:17 +01:00
break ;
2008-06-03 20:35:58 +02:00
case SGT_TTD :
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " TTD savegame " ) ;
2008-06-03 20:35:58 +02:00
break ;
case SGT_TTDP1 :
case SGT_TTDP2 :
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " TTDP savegame, %s format " ,
2023-05-01 19:14:31 +02:00
lc . oldver . type = = SGT_TTDP1 ? " old " : " new " ) ;
if ( lc . oldver . version ! = 0 ) {
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " , TTDP version %u.%u.%u.%u " ,
2023-05-01 19:14:31 +02: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 ) ) ;
2008-06-03 20:35:58 +02:00
}
break ;
}
break ;
2009-02-08 13:25:13 +01:00
case GLCT_SETTING :
2023-02-25 16:59:43 +01:00
/* A setting with the SF_NO_NETWORK flag got changed; these settings usually affect NewGRFs, such as road side or wagon speed limits. */
2023-05-01 19:14:31 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " Setting changed: %s : %d -> %d " , lc . setting . name , lc . setting . oldval , lc . setting . newval ) ;
2008-06-03 20:35:58 +02:00
break ;
2010-10-16 23:13:55 +02:00
case GLCT_GRFADD : {
2023-02-25 16:59:43 +01: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. */
2023-05-01 19:14:31 +02:00
const GRFConfig * gc = FindGRFConfig ( lc . grfadd . grfid , FGCM_EXACT , lc . grfadd . md5sum ) ;
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " Added NewGRF: " ) ;
2023-05-01 19:14:31 +02:00
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc . grfadd . grfid , lc . grfadd . md5sum , gc ) ;
GrfIDMapping : : Pair * gm = grf_names . Find ( lc . grfrem . grfid ) ;
2014-04-25 17:25:59 +02:00
if ( gm ! = grf_names . End ( ) & & ! gm - > second . was_missing ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was already added! " ) ;
2023-05-01 19:14:31 +02:00
grf_names [ lc . grfadd . grfid ] = gc ;
2008-06-03 20:35:58 +02:00
break ;
2010-10-16 23:13:55 +02:00
}
2008-06-03 20:35:58 +02:00
2010-10-16 23:20:46 +02:00
case GLCT_GRFREM : {
2023-02-25 16:59:43 +01:00
/* A NewGRF got removed from the game, either manually or by it missing when loading the game. */
2023-05-01 19:14:31 +02:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc . grfrem . grfid ) ;
buf + = seprintf ( buf , lastof ( buffer ) , la . at = = GLAT_LOAD ? " Missing NewGRF: " : " Removed NewGRF: " ) ;
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc . grfrem . grfid , nullptr , gm ! = grf_names . End ( ) ? gm - > second . gc : nullptr ) ;
2010-10-17 00:46:53 +02:00
if ( gm = = grf_names . End ( ) ) {
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2010-10-17 00:46:53 +02:00
} else {
2023-05-01 19:14:31 +02:00
if ( la . at = = GLAT_LOAD ) {
2010-10-17 00:46:53 +02:00
/* Missing grfs on load are not removed from the configuration */
gm - > second . was_missing = true ;
} else {
grf_names . Erase ( gm ) ;
}
}
2008-06-03 20:35:58 +02:00
break ;
2010-10-16 23:20:46 +02:00
}
2008-06-03 20:35:58 +02:00
2010-10-16 23:13:55 +02:00
case GLCT_GRFCOMPAT : {
2023-02-25 16:59:43 +01:00
/* Another version of the same NewGRF got loaded. */
2023-05-01 19:14:31 +02:00
const GRFConfig * gc = FindGRFConfig ( lc . grfadd . grfid , FGCM_EXACT , lc . grfadd . md5sum ) ;
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " Compatible NewGRF loaded: " ) ;
2023-05-01 19:14:31 +02:00
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! " ) ;
grf_names [ lc . grfcompat . grfid ] = gc ;
2008-06-03 20:35:58 +02:00
break ;
2010-10-16 23:13:55 +02:00
}
2008-06-03 20:35:58 +02:00
2010-10-16 23:20:46 +02:00
case GLCT_GRFPARAM : {
2023-02-25 16:59:43 +01:00
/* A parameter of a NewGRF got changed after the game was started. */
2023-05-01 19:14:31 +02:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc . grfrem . grfid ) ;
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " GRF parameter changed: " ) ;
2023-05-01 19:14:31 +02:00
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc . grfparam . grfid , nullptr , gm ! = grf_names . End ( ) ? gm - > second . gc : nullptr ) ;
2014-04-25 17:25:59 +02:00
if ( gm = = grf_names . End ( ) ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2008-06-03 20:35:58 +02:00
break ;
2010-10-16 23:20:46 +02:00
}
2008-06-03 20:35:58 +02:00
2010-10-16 23:20:46 +02:00
case GLCT_GRFMOVE : {
2023-02-25 16:59:43 +01:00
/* The order of NewGRFs got changed, which might cause some other NewGRFs to behave differently. */
2023-05-01 19:14:31 +02:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc . grfrem . grfid ) ;
2014-04-25 17:25:59 +02:00
buf + = seprintf ( buf , lastof ( buffer ) , " GRF order changed: %08X moved %d places %s " ,
2023-05-01 19:14:31 +02:00
BSWAP32 ( lc . grfmove . grfid ) , abs ( lc . grfmove . offset ) , lc . grfmove . offset > = 0 ? " down " : " up " ) ;
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc . grfmove . grfid , nullptr , gm ! = grf_names . End ( ) ? gm - > second . gc : nullptr ) ;
2014-04-25 17:25:59 +02:00
if ( gm = = grf_names . End ( ) ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2008-06-03 20:35:58 +02:00
break ;
2010-10-16 23:20:46 +02:00
}
2008-07-24 17:19:26 +02:00
2010-10-16 23:20:46 +02:00
case GLCT_GRFBUG : {
2023-02-25 16:59:43 +01:00
/* A specific bug in a NewGRF, that could cause wide spread problems, has been noted during the execution of the game. */
2023-05-01 19:14:31 +02:00
GrfIDMapping : : Pair * gm = grf_names . Find ( lc . grfrem . grfid ) ;
assert ( lc . grfbug . bug = = GBUG_VEH_LENGTH ) ;
2023-03-25 20:40:59 +01:00
2023-05-01 19:14:31 +02: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 ) ;
buf = PrintGrfInfo ( buf , lastof ( buffer ) , lc . grfbug . grfid , nullptr , gm ! = grf_names . End ( ) ? gm - > second . gc : nullptr ) ;
2014-04-25 17:25:59 +02:00
if ( gm = = grf_names . End ( ) ) buf + = seprintf ( buf , lastof ( buffer ) , " . Gamelog inconsistency: GrfID was never added! " ) ;
2010-10-16 23:13:55 +02:00
break ;
2010-10-16 23:20:46 +02:00
}
2009-03-30 02:21:43 +02:00
case GLCT_EMERGENCY :
2023-02-25 16:59:43 +01: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 02:21:43 +02:00
break ;
2008-06-03 20:35:58 +02:00
}
2014-04-25 17:25:59 +02:00
proc ( buffer ) ;
2008-06-03 20:35:58 +02:00
}
}
proc ( " ---- gamelog end ---- " ) ;
}
2011-05-02 19:42:12 +02:00
/** Print the gamelog data to the console. */
2023-05-01 19:14:31 +02:00
void Gamelog : : PrintConsole ( )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
this - > Print ( [ ] ( const char * s ) {
2023-01-27 22:40:30 +01:00
IConsolePrint ( CC_WARNING , s ) ;
} ) ;
2008-06-03 20:35:58 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Prints gamelog to debug output . Code is executed even when
2008-07-18 14:11:46 +02: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
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : PrintDebug ( int level )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
this - > Print ( [ level ] ( const char * s ) {
2023-01-27 22:40:30 +01:00
Debug ( gamelog , level , " {} " , s ) ;
} ) ;
2008-06-03 20:35:58 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Allocates new LoggedChange and new LoggedAction if needed .
2019-04-10 23:07:06 +02:00
* If there is no action active , nullptr is returned .
2008-06-03 20:35:58 +02:00
* @ param ct type of change
2019-04-10 23:07:06 +02:00
* @ return new LoggedChange , or nullptr if there is no action active
2008-06-03 20:35:58 +02:00
*/
2023-05-01 19:14:31 +02:00
LoggedChange * Gamelog : : Change ( GamelogChangeType ct )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
if ( this - > current_action = = nullptr ) {
if ( this - > action_type = = GLAT_NONE ) return nullptr ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
this - > current_action = & this - > data - > action . emplace_back ( ) ;
this - > current_action - > at = this - > action_type ;
this - > current_action - > tick = TimerGameTick : : counter ;
2008-06-03 20:35:58 +02:00
}
2023-05-01 19:14:31 +02:00
LoggedChange * lc = & this - > current_action - > change . emplace_back ( ) ;
2008-06-03 20:35:58 +02:00
lc - > ct = ct ;
return lc ;
}
2010-08-01 21:22:34 +02:00
/**
* Logs a emergency savegame
2009-03-30 02:21:43 +02:00
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : Emergency ( )
2009-03-30 02:21:43 +02:00
{
2009-09-14 21:30:13 +02:00
/* Terminate any active action */
2023-05-01 19:14:31 +02:00
if ( this - > action_type ! = GLAT_NONE ) this - > StopAction ( ) ;
this - > StartAction ( GLAT_EMERGENCY ) ;
this - > Change ( GLCT_EMERGENCY ) ;
this - > StopAction ( ) ;
2009-03-30 02:21:43 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Finds out if current game is a loaded emergency savegame .
2009-03-30 02:21:43 +02:00
*/
2023-05-01 19:14:31 +02:00
bool Gamelog : : TestEmergency ( )
2009-03-30 02:21:43 +02:00
{
2023-05-01 19:14:31 +02:00
for ( const LoggedAction & la : this - > data - > action ) {
for ( const LoggedChange & lc : la . change ) {
if ( lc . ct = = GLCT_EMERGENCY ) return true ;
2009-03-30 02:21:43 +02:00
}
}
2023-05-01 19:14:31 +02:00
return false ;
2009-03-30 02:21:43 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Logs a change in game revision
2008-06-03 20:35:58 +02:00
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : Revision ( )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_START | | this - > action_type = = GLAT_LOAD ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_REVISION ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02:00
2009-01-27 15:31:33 +01:00
memset ( lc - > revision . text , 0 , sizeof ( lc - > revision . text ) ) ;
2019-01-27 16:13:28 +01:00
strecpy ( lc - > revision . text , GetGamelogRevisionString ( ) , lastof ( lc - > revision . text ) ) ;
2008-06-03 20:35:58 +02:00
lc - > revision . slver = SAVEGAME_VERSION ;
lc - > revision . modified = _openttd_revision_modified ;
lc - > revision . newgrf = _openttd_newgrf_version ;
}
2010-08-01 21:22:34 +02:00
/**
* Logs a change in game mode ( scenario editor or game )
2008-06-03 20:35:58 +02:00
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : Mode ( )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_START | | this - > action_type = = GLAT_LOAD | | this - > action_type = = GLAT_CHEAT ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_MODE ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02:00
lc - > mode . mode = _game_mode ;
lc - > mode . landscape = _settings_game . game_creation . landscape ;
}
2010-08-01 21:22:34 +02:00
/**
* Logs loading from savegame without gamelog
2008-06-03 20:35:58 +02:00
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : Oldver ( )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_LOAD ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_OLDVER ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02: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 21:22:34 +02:00
/**
* Logs change in game settings . Only non - networksafe settings are logged
2009-02-08 13:25:13 +01:00
* @ param name setting name
* @ param oldval old setting value
* @ param newval new setting value
2008-06-03 20:35:58 +02:00
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : Setting ( const std : : string & name , int32 oldval , int32 newval )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_SETTING ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_SETTING ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02:00
2021-05-30 11:51:21 +02:00
lc - > setting . name = stredup ( name . c_str ( ) ) ;
2009-02-08 13:25:13 +01:00
lc - > setting . oldval = oldval ;
lc - > setting . newval = newval ;
2008-06-03 20:35:58 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Finds out if current revision is different than last revision stored in the savegame .
2008-06-03 20:35:58 +02:00
* Appends GLCT_REVISION when the revision string changed
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : TestRevision ( )
2008-06-03 20:35:58 +02:00
{
2019-04-10 23:07:06 +02:00
const LoggedChange * rev = nullptr ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
for ( const LoggedAction & la : this - > data - > action ) {
for ( const LoggedChange & lc : la . change ) {
if ( lc . ct = = GLCT_REVISION ) rev = & lc ;
2008-06-03 20:35:58 +02:00
}
}
2019-04-10 23:07:06 +02:00
if ( rev = = nullptr | | strcmp ( rev - > revision . text , GetGamelogRevisionString ( ) ) ! = 0 | |
2008-06-03 20:35:58 +02:00
rev - > revision . modified ! = _openttd_revision_modified | |
rev - > revision . newgrf ! = _openttd_newgrf_version ) {
2023-05-01 19:14:31 +02:00
this - > Revision ( ) ;
2008-06-03 20:35:58 +02:00
}
}
2010-08-01 21:22:34 +02:00
/**
* Finds last stored game mode or landscape .
2008-06-03 20:35:58 +02:00
* Any change is logged
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : TestMode ( )
2008-06-03 20:35:58 +02:00
{
2019-04-10 23:07:06 +02:00
const LoggedChange * mode = nullptr ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
for ( const LoggedAction & la : this - > data - > action ) {
for ( const LoggedChange & lc : la . change ) {
if ( lc . ct = = GLCT_MODE ) mode = & lc ;
2008-06-03 20:35:58 +02:00
}
}
2023-05-01 19:14:31 +02:00
if ( mode = = nullptr | | mode - > mode . mode ! = _game_mode | | mode - > mode . landscape ! = _settings_game . game_creation . landscape ) this - > Mode ( ) ;
2008-06-03 20:35:58 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Logs triggered GRF bug .
2008-07-24 17:19:26 +02:00
* @ param grfid ID of problematic GRF
* @ param bug type of bug , @ see enum GRFBugs
* @ param data additional data
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : GRFBug ( uint32 grfid , byte bug , uint64 data )
2008-07-24 17:19:26 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_GRFBUG ) ;
2008-07-24 17:19:26 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_GRFBUG ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-07-24 17:19:26 +02:00
lc - > grfbug . data = data ;
lc - > grfbug . grfid = grfid ;
lc - > grfbug . bug = bug ;
}
2010-08-01 21:22:34 +02:00
/**
* Logs GRF bug - rail vehicle has different length after reversing .
2008-07-24 17:19:26 +02: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 11:51:14 +02:00
* @ param grfid the broken NewGRF
* @ param internal_id the internal ID of whatever ' s broken in the NewGRF
2008-07-24 17:19:26 +02:00
* @ return true iff a unique record was done
*/
2023-05-01 19:14:31 +02:00
bool Gamelog : : GRFBugReverse ( uint32 grfid , uint16 internal_id )
{
for ( const LoggedAction & la : this - > data - > action ) {
for ( const LoggedChange & lc : la . change ) {
if ( lc . ct = = GLCT_GRFBUG & & lc . grfbug . grfid = = grfid & &
lc . grfbug . bug = = GBUG_VEH_LENGTH & & lc . grfbug . data = = internal_id ) {
2008-07-24 17:19:26 +02:00
return false ;
}
}
}
2023-05-01 19:14:31 +02:00
this - > StartAction ( GLAT_GRFBUG ) ;
this - > GRFBug ( grfid , GBUG_VEH_LENGTH , internal_id ) ;
this - > StopAction ( ) ;
2008-07-24 17:19:26 +02:00
return true ;
}
2010-08-01 21:22:34 +02:00
/**
* Decides if GRF should be logged
2008-06-03 20:35:58 +02: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 21:22:34 +02:00
/**
* Logs removal of a GRF
2008-06-03 20:35:58 +02:00
* @ param grfid ID of removed GRF
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : GRFRemove ( uint32 grfid )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_LOAD | | this - > action_type = = GLAT_GRF ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_GRFREM ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02:00
lc - > grfrem . grfid = grfid ;
}
2010-08-01 21:22:34 +02:00
/**
* Logs adding of a GRF
2008-06-03 20:35:58 +02:00
* @ param newg added GRF
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : GRFAdd ( const GRFConfig * newg )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_LOAD | | this - > action_type = = GLAT_START | | this - > action_type = = GLAT_GRF ) ;
2008-06-03 20:35:58 +02:00
if ( ! IsLoggableGrfConfig ( newg ) ) return ;
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_GRFADD ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02:00
2010-03-12 20:18:26 +01:00
lc - > grfadd = newg - > ident ;
2008-06-03 20:35:58 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Logs loading compatible GRF
2008-06-03 20:35:58 +02:00
* ( the same ID , but different MD5 hash )
* @ param newg new ( updated ) GRF
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : GRFCompatible ( const GRFIdentifier * newg )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_LOAD | | this - > action_type = = GLAT_GRF ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_GRFCOMPAT ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02:00
2010-03-12 20:18:26 +01:00
lc - > grfcompat = * newg ;
2008-06-03 20:35:58 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* Logs changing GRF order
2008-06-03 20:35:58 +02:00
* @ param grfid GRF that is moved
* @ param offset how far it is moved , positive = moved down
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : GRFMove ( uint32 grfid , int32 offset )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_GRF ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_GRFMOVE ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02:00
lc - > grfmove . grfid = grfid ;
lc - > grfmove . offset = offset ;
}
2010-08-01 21:22:34 +02:00
/**
* Logs change in GRF parameters .
2008-06-03 20:35:58 +02:00
* Details about parameters changed are not stored
* @ param grfid ID of GRF to store
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : GRFParameters ( uint32 grfid )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_GRF ) ;
2008-06-03 20:35:58 +02:00
2023-05-01 19:14:31 +02:00
LoggedChange * lc = this - > Change ( GLCT_GRFPARAM ) ;
2019-04-10 23:07:06 +02:00
if ( lc = = nullptr ) return ;
2008-06-03 20:35:58 +02:00
lc - > grfparam . grfid = grfid ;
}
2010-08-01 21:22:34 +02:00
/**
* Logs adding of list of GRFs .
2008-06-03 20:35:58 +02:00
* Useful when old savegame is loaded or when new game is started
* @ param newg head of GRF linked list
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : GRFAddList ( const GRFConfig * newg )
2008-06-03 20:35:58 +02:00
{
2023-05-01 19:14:31 +02:00
assert ( this - > action_type = = GLAT_START | | this - > action_type = = GLAT_LOAD ) ;
2008-06-03 20:35:58 +02:00
2019-04-10 23:07:06 +02:00
for ( ; newg ! = nullptr ; newg = newg - > next ) {
2023-05-01 19:14:31 +02:00
this - > GRFAdd ( newg ) ;
2008-06-03 20:35:58 +02:00
}
}
2010-08-01 21:22:34 +02:00
/**
* Generates GRFList
2008-06-03 20:35:58 +02:00
* @ param grfc head of GRF linked list
*/
2023-01-20 17:00:18 +01:00
static std : : vector < const GRFConfig * > GenerateGRFList ( const GRFConfig * grfc )
2008-06-03 20:35:58 +02:00
{
2023-01-20 17:00:18 +01:00
std : : vector < const GRFConfig * > list ;
2019-04-10 23:07:06 +02:00
for ( const GRFConfig * g = grfc ; g ! = nullptr ; g = g - > next ) {
2023-01-20 17:00:18 +01:00
if ( IsLoggableGrfConfig ( g ) ) list . push_back ( g ) ;
2008-06-03 20:35:58 +02:00
}
return list ;
}
2010-08-01 21:22:34 +02:00
/**
* Compares two NewGRF lists and logs any change
2008-06-03 20:35:58 +02:00
* @ param oldc original GRF list
* @ param newc new GRF list
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : GRFUpdate ( const GRFConfig * oldc , const GRFConfig * newc )
2008-06-03 20:35:58 +02:00
{
2023-01-20 17:00:18 +01:00
std : : vector < const GRFConfig * > ol = GenerateGRFList ( oldc ) ;
std : : vector < const GRFConfig * > nl = GenerateGRFList ( newc ) ;
2008-06-03 20:35:58 +02:00
uint o = 0 , n = 0 ;
2023-01-20 17:00:18 +01:00
while ( o < ol . size ( ) & & n < nl . size ( ) ) {
const GRFConfig * og = ol [ o ] ;
const GRFConfig * ng = nl [ n ] ;
2008-06-03 20:35:58 +02:00
2010-02-25 21:05:31 +01:00
if ( og - > ident . grfid ! = ng - > ident . grfid ) {
2008-06-03 20:35:58 +02:00
uint oi , ni ;
2023-01-20 17:00:18 +01:00
for ( oi = 0 ; oi < ol . size ( ) ; oi + + ) {
if ( ol [ oi ] - > ident . grfid = = nl [ n ] - > ident . grfid ) break ;
2008-06-03 20:35:58 +02:00
}
if ( oi < o ) {
/* GRF was moved, this change has been logged already */
n + + ;
continue ;
}
2023-01-20 17:00:18 +01:00
if ( oi = = ol . size ( ) ) {
2008-06-03 20:35:58 +02:00
/* GRF couldn't be found in the OLD list, GRF was ADDED */
2023-01-20 17:00:18 +01:00
this - > GRFAdd ( nl [ n + + ] ) ;
2008-06-03 20:35:58 +02:00
continue ;
}
2023-01-20 17:00:18 +01:00
for ( ni = 0 ; ni < nl . size ( ) ; ni + + ) {
if ( nl [ ni ] - > ident . grfid = = ol [ o ] - > ident . grfid ) break ;
2008-06-03 20:35:58 +02:00
}
if ( ni < n ) {
/* GRF was moved, this change has been logged already */
o + + ;
continue ;
}
2023-01-20 17:00:18 +01:00
if ( ni = = nl . size ( ) ) {
2008-06-03 20:35:58 +02:00
/* GRF couldn't be found in the NEW list, GRF was REMOVED */
2023-01-20 17:00:18 +01:00
this - > GRFRemove ( ol [ o + + ] - > ident . grfid ) ;
2008-06-03 20:35:58 +02:00
continue ;
}
/* o < oi < ol->n
* n < ni < nl - > n */
2023-01-20 17:00:18 +01:00
assert ( ni > n & & ni < nl . size ( ) ) ;
assert ( oi > o & & oi < ol . size ( ) ) ;
2008-06-03 20:35:58 +02:00
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 */
2023-01-20 17:00:18 +01:00
this - > GRFMove ( ol [ o + + ] - > ident . grfid , ni ) ;
2008-06-03 20:35:58 +02:00
} else {
2023-01-20 17:00:18 +01:00
this - > GRFMove ( nl [ n + + ] - > ident . grfid , - ( int ) oi ) ;
2008-06-03 20:35:58 +02:00
}
} else {
2010-02-25 21:05:31 +01:00
if ( memcmp ( og - > ident . md5sum , ng - > ident . md5sum , sizeof ( og - > ident . md5sum ) ) ! = 0 ) {
2008-06-03 20:35:58 +02:00
/* md5sum changed, probably loading 'compatible' GRF */
2023-01-20 17:00:18 +01:00
this - > GRFCompatible ( & nl [ n ] - > ident ) ;
2008-06-03 20:35:58 +02:00
}
if ( og - > num_params ! = ng - > num_params | | memcmp ( og - > param , ng - > param , og - > num_params * sizeof ( og - > param [ 0 ] ) ) ! = 0 ) {
2023-01-20 17:00:18 +01:00
this - > GRFParameters ( ol [ o ] - > ident . grfid ) ;
2008-06-03 20:35:58 +02:00
}
o + + ;
n + + ;
}
}
2023-01-20 17:00:18 +01:00
while ( o < ol . size ( ) ) this - > GRFRemove ( ol [ o + + ] - > ident . grfid ) ; // remaining GRFs were removed ...
while ( n < nl . size ( ) ) this - > GRFAdd ( nl [ n + + ] ) ; // ... or added
2008-06-03 20:35:58 +02:00
}
2011-10-30 14:47:45 +01:00
/**
* Get some basic information from the given gamelog .
2018-10-28 03:17:36 +01: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 14:47:45 +01:00
*/
2023-05-01 19:14:31 +02:00
void Gamelog : : Info ( uint32 * last_ottd_rev , byte * ever_modified , bool * removed_newgrfs )
2011-10-30 14:47:45 +01:00
{
2023-05-01 19:14:31 +02:00
for ( const LoggedAction & la : this - > data - > action ) {
for ( const LoggedChange & lc : la . change ) {
switch ( lc . ct ) {
2011-10-30 14:47:45 +01:00
default : break ;
case GLCT_REVISION :
2023-05-01 19:14:31 +02:00
* last_ottd_rev = lc . revision . newgrf ;
* ever_modified = std : : max ( * ever_modified , lc . revision . modified ) ;
2011-10-30 14:47:45 +01:00
break ;
case GLCT_GRFREM :
* removed_newgrfs = true ;
break ;
}
}
}
}
2023-05-01 19:14:31 +02:00
/**
* Try to find the overridden GRF identifier of the given GRF .
* @ param c the GRF to get the ' previous ' version of .
* @ return the GRF identifier or \ a c if none could be found .
*/
const GRFIdentifier * Gamelog : : GetOverriddenIdentifier ( const GRFConfig * c )
{
assert ( c ! = nullptr ) ;
const LoggedAction & la = this - > data - > action . back ( ) ;
if ( la . at ! = GLAT_LOAD ) return & c - > ident ;
for ( const LoggedChange & lc : la . change ) {
if ( lc . ct = = GLCT_GRFCOMPAT & & lc . grfcompat . grfid = = c - > ident . grfid ) return & lc . grfcompat ;
}
return & c - > ident ;
}