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-05-06 15:11:33 +00:00
/** @file newgrf_config.cpp Finding NewGRFs and configuring them. */
2007-03-21 03:06:21 +00:00
2006-12-04 08:30:04 +00:00
# include "stdafx.h"
# include "debug.h"
2009-08-21 20:15:17 +00:00
# include "3rdparty/md5/md5.h"
2006-12-04 08:30:04 +00:00
# include "newgrf.h"
2009-09-17 21:14:16 +00:00
# include "network/network_func.h"
2008-09-03 07:51:07 +00:00
# include "gfx_func.h"
2010-07-31 09:33:39 +00:00
# include "newgrf_text.h"
2010-07-31 12:15:12 +00:00
# include "window_func.h"
2011-08-21 12:53:00 +00:00
# include "progress.h"
2011-08-24 12:11:10 +00:00
# include "video/video_driver.hpp"
2011-11-10 18:07:56 +00:00
# include "strings_func.h"
2012-02-12 10:51:43 +00:00
# include "textfile_gui.h"
2019-03-17 00:59:46 +00:00
# include "thread.h"
# include "newgrf_config.h"
2020-05-17 21:31:47 +00:00
# include "newgrf_text.h"
2006-12-04 08:30:04 +00:00
2008-08-31 10:50:05 +00:00
# include "fileio_func.h"
2006-12-04 08:30:04 +00:00
# include "fios.h"
2019-09-22 23:26:45 +00:00
# include "thread.h"
# include <mutex>
# include <condition_variable>
# if defined(__MINGW32__)
2019-10-12 00:17:41 +00:00
# include "3rdparty/mingw-std-threads/mingw.mutex.h"
# include "3rdparty/mingw-std-threads/mingw.condition_variable.h"
2019-09-22 23:26:45 +00:00
# endif
2014-04-23 20:13:33 +00:00
# include "safeguards.h"
2011-03-03 18:47:46 +00:00
2010-07-31 09:32:44 +00:00
/**
* Create a new GRFConfig .
* @ param filename Set the filename of this GRFConfig to filename . The argument
* is copied so the original string isn ' t needed after the constructor .
*/
2010-07-31 09:35:49 +00:00
GRFConfig : : GRFConfig ( const char * filename ) :
num_valid_params ( lengthof ( param ) )
2010-02-25 20:06:11 +00:00
{
2019-04-10 21:07:06 +00:00
if ( filename ! = nullptr ) this - > filename = stredup ( filename ) ;
2010-02-25 20:06:11 +00:00
}
2010-07-31 09:32:44 +00:00
/**
* Create a new GRFConfig that is a deep copy of an existing config .
* @ param config The GRFConfig object to make a copy of .
*/
GRFConfig : : GRFConfig ( const GRFConfig & config ) :
2010-08-01 19:52:51 +00:00
ZeroedMemoryAllocator ( ) ,
2010-07-31 09:32:44 +00:00
ident ( config . ident ) ,
2011-03-03 18:47:46 +00:00
name ( config . name ) ,
info ( config . info ) ,
2011-12-11 12:55:04 +00:00
url ( config . url ) ,
2010-07-31 14:40:50 +00:00
version ( config . version ) ,
2010-10-17 12:14:49 +00:00
min_loadable_version ( config . min_loadable_version ) ,
2010-11-09 21:57:02 +00:00
flags ( config . flags & ~ ( 1 < < GCF_COPY ) ) ,
2010-07-31 09:32:44 +00:00
status ( config . status ) ,
grf_bugs ( config . grf_bugs ) ,
num_params ( config . num_params ) ,
2010-07-31 09:35:49 +00:00
num_valid_params ( config . num_valid_params ) ,
2010-08-23 21:47:07 +00:00
palette ( config . palette ) ,
has_param_defaults ( config . has_param_defaults )
2010-07-31 09:32:44 +00:00
{
MemCpyT < uint8 > ( this - > original_md5sum , config . original_md5sum , lengthof ( this - > original_md5sum ) ) ;
MemCpyT < uint32 > ( this - > param , config . param , lengthof ( this - > param ) ) ;
2019-04-10 21:07:06 +00:00
if ( config . filename ! = nullptr ) this - > filename = stredup ( config . filename ) ;
if ( config . error ! = nullptr ) this - > error = new GRFError ( * config . error ) ;
2018-09-23 11:23:54 +00:00
for ( uint i = 0 ; i < config . param_info . size ( ) ; i + + ) {
2019-04-10 21:07:06 +00:00
if ( config . param_info [ i ] = = nullptr ) {
this - > param_info . push_back ( nullptr ) ;
2010-07-31 09:51:04 +00:00
} else {
2019-02-18 22:39:06 +00:00
this - > param_info . push_back ( new GRFParameterInfo ( * config . param_info [ i ] ) ) ;
2010-07-31 09:51:04 +00:00
}
}
2010-07-31 09:32:44 +00:00
}
/** Cleanup a GRFConfig object. */
2010-02-25 20:06:11 +00:00
GRFConfig : : ~ GRFConfig ( )
{
2014-04-25 15:40:32 +00:00
/* GCF_COPY as in NOT stredupped/alloced the filename */
2010-02-25 20:06:11 +00:00
if ( ! HasBit ( this - > flags , GCF_COPY ) ) {
free ( this - > filename ) ;
delete this - > error ;
}
2010-07-31 09:51:04 +00:00
2018-09-23 11:23:54 +00:00
for ( uint i = 0 ; i < this - > param_info . size ( ) ; i + + ) delete this - > param_info [ i ] ;
2010-02-25 20:06:11 +00:00
}
2006-12-04 08:30:04 +00:00
2014-05-24 19:13:34 +00:00
/**
* Copy the parameter information from the \ a src config .
* @ param src Source config .
*/
void GRFConfig : : CopyParams ( const GRFConfig & src )
{
this - > num_params = src . num_params ;
this - > num_valid_params = src . num_valid_params ;
MemCpyT < uint32 > ( this - > param , src . param , lengthof ( this - > param ) ) ;
}
2010-02-28 20:28:08 +00:00
/**
* Get the name of this grf . In case the name isn ' t known
* the filename is returned .
* @ return The name of filename of this grf .
*/
const char * GRFConfig : : GetName ( ) const
{
2020-05-17 21:31:47 +00:00
const char * name = GetGRFStringFromGRFText ( this - > name ) ;
2010-07-31 09:33:39 +00:00
return StrEmpty ( name ) ? this - > filename : name ;
2010-02-28 20:28:08 +00:00
}
/**
* Get the grf info .
* @ return A string with a description of this grf .
*/
const char * GRFConfig : : GetDescription ( ) const
{
2020-05-17 21:31:47 +00:00
return GetGRFStringFromGRFText ( this - > info ) ;
2010-02-28 20:28:08 +00:00
}
2011-12-11 12:55:04 +00:00
/**
* Get the grf url .
* @ return A string with an url of this grf .
*/
const char * GRFConfig : : GetURL ( ) const
{
2020-05-17 21:31:47 +00:00
return GetGRFStringFromGRFText ( this - > url ) ;
2011-12-11 12:55:04 +00:00
}
2010-08-23 21:47:07 +00:00
/** Set the default value for all parameters as specified by action14. */
void GRFConfig : : SetParameterDefaults ( )
{
2010-08-23 22:25:50 +00:00
this - > num_params = 0 ;
MemSetT < uint32 > ( this - > param , 0 , lengthof ( this - > param ) ) ;
2010-08-23 21:47:07 +00:00
if ( ! this - > has_param_defaults ) return ;
2018-09-23 11:23:54 +00:00
for ( uint i = 0 ; i < this - > param_info . size ( ) ; i + + ) {
2019-04-10 21:07:06 +00:00
if ( this - > param_info [ i ] = = nullptr ) continue ;
2010-08-23 21:47:07 +00:00
this - > param_info [ i ] - > SetValue ( this , this - > param_info [ i ] - > def_value ) ;
}
}
2010-07-31 09:36:09 +00:00
/**
* Set the palette of this GRFConfig to something suitable .
* That is either the setting coming from the NewGRF or
* the globally used palette .
*/
void GRFConfig : : SetSuitablePalette ( )
{
2010-07-31 09:44:38 +00:00
PaletteType pal ;
switch ( this - > palette & GRFP_GRF_MASK ) {
case GRFP_GRF_DOS : pal = PAL_DOS ; break ;
case GRFP_GRF_WINDOWS : pal = PAL_WINDOWS ; break ;
2011-05-03 20:30:26 +00:00
default : pal = _settings_client . gui . newgrf_default_palette = = 1 ? PAL_WINDOWS : PAL_DOS ; break ;
2010-07-31 09:44:38 +00:00
}
2010-07-31 09:40:43 +00:00
SB ( this - > palette , GRFP_USE_BIT , 1 , pal = = PAL_WINDOWS ? GRFP_USE_WINDOWS : GRFP_USE_DOS ) ;
2010-07-31 09:36:09 +00:00
}
2012-06-01 15:20:18 +00:00
/**
* Finalize Action 14 info after file scan is finished .
*/
void GRFConfig : : FinalizeParameterInfo ( )
{
2019-02-17 11:20:52 +00:00
for ( GRFParameterInfo * info : this - > param_info ) {
2019-04-10 21:07:06 +00:00
if ( info = = nullptr ) continue ;
2019-02-17 11:20:52 +00:00
info - > Finalize ( ) ;
2012-06-01 15:20:18 +00:00
}
}
2006-12-04 08:30:04 +00:00
GRFConfig * _all_grfs ;
GRFConfig * _grfconfig ;
GRFConfig * _grfconfig_newgame ;
2006-12-12 19:38:41 +00:00
GRFConfig * _grfconfig_static ;
2017-01-14 18:30:26 +00:00
uint _missing_extra_graphics = 0 ;
2006-12-04 08:30:04 +00:00
2016-12-08 23:48:53 +00:00
bool _grf_bug_too_many_strings = false ;
2010-07-31 09:32:44 +00:00
/**
* Construct a new GRFError .
* @ param severity The severity of this error .
* @ param message The actual error - string .
*/
2010-02-25 15:54:40 +00:00
GRFError : : GRFError ( StringID severity , StringID message ) :
message ( message ) ,
2020-05-17 21:31:54 +00:00
severity ( severity ) ,
param_value ( )
2010-02-25 15:54:40 +00:00
{
}
2010-07-31 09:32:44 +00:00
/**
* Create a new GRFError that is a deep copy of an existing error message .
* @ param error The GRFError object to make a copy of .
*/
GRFError : : GRFError ( const GRFError & error ) :
custom_message ( error . custom_message ) ,
data ( error . data ) ,
message ( error . message ) ,
2012-01-15 17:33:35 +00:00
severity ( error . severity )
2010-07-31 09:32:44 +00:00
{
memcpy ( this - > param_value , error . param_value , sizeof ( this - > param_value ) ) ;
}
2010-07-31 09:51:04 +00:00
/**
* Create a new empty GRFParameterInfo object .
* @ param nr The newgrf parameter that is changed .
*/
GRFParameterInfo : : GRFParameterInfo ( uint nr ) :
2020-05-17 21:31:47 +00:00
name ( ) ,
desc ( ) ,
2010-07-31 09:51:04 +00:00
type ( PTYPE_UINT_ENUM ) ,
min_value ( 0 ) ,
max_value ( UINT32_MAX ) ,
2010-08-23 21:47:07 +00:00
def_value ( 0 ) ,
2010-07-31 09:51:04 +00:00
param_nr ( nr ) ,
first_bit ( 0 ) ,
2013-11-23 13:12:19 +00:00
num_bit ( 32 ) ,
2020-05-17 21:31:47 +00:00
value_names ( ) ,
2013-11-23 13:12:19 +00:00
complete_labels ( false )
2010-07-31 09:51:04 +00:00
{ }
/**
* Create a new GRFParameterInfo object that is a deep copy of an existing
* parameter info object .
* @ param info The GRFParameterInfo object to make a copy of .
*/
GRFParameterInfo : : GRFParameterInfo ( GRFParameterInfo & info ) :
2020-05-17 21:31:47 +00:00
name ( info . name ) ,
desc ( info . desc ) ,
2010-07-31 09:51:04 +00:00
type ( info . type ) ,
min_value ( info . min_value ) ,
max_value ( info . max_value ) ,
2010-08-23 21:47:07 +00:00
def_value ( info . def_value ) ,
2010-07-31 09:51:04 +00:00
param_nr ( info . param_nr ) ,
first_bit ( info . first_bit ) ,
2012-06-01 15:20:18 +00:00
num_bit ( info . num_bit ) ,
2020-05-17 21:31:47 +00:00
value_names ( info . value_names ) ,
2012-06-01 15:20:18 +00:00
complete_labels ( info . complete_labels )
2010-07-31 09:51:04 +00:00
{
}
2010-07-31 12:15:12 +00:00
/**
* Get the value of this user - changeable parameter from the given config .
* @ param config The GRFConfig to get the value from .
* @ return The value of this parameter .
*/
uint32 GRFParameterInfo : : GetValue ( struct GRFConfig * config ) const
{
/* GB doesn't work correctly with nbits == 32, so handle that case here. */
if ( this - > num_bit = = 32 ) return config - > param [ this - > param_nr ] ;
return GB ( config - > param [ this - > param_nr ] , this - > first_bit , this - > num_bit ) ;
}
/**
* Set the value of this user - changeable parameter in the given config .
* @ param config The GRFConfig to set the value in .
* @ param value The new value .
*/
void GRFParameterInfo : : SetValue ( struct GRFConfig * config , uint32 value )
{
/* SB doesn't work correctly with nbits == 32, so handle that case here. */
if ( this - > num_bit = = 32 ) {
config - > param [ this - > param_nr ] = value ;
} else {
SB ( config - > param [ this - > param_nr ] , this - > first_bit , this - > num_bit , value ) ;
}
2021-01-08 10:16:18 +00:00
config - > num_params = std : : max < uint > ( config - > num_params , this - > param_nr + 1 ) ;
2011-12-19 20:50:21 +00:00
SetWindowDirty ( WC_GAME_OPTIONS , WN_GAME_OPTIONS_NEWGRF_STATE ) ;
2010-07-31 12:15:12 +00:00
}
2012-06-01 15:20:18 +00:00
/**
* Finalize Action 14 info after file scan is finished .
*/
void GRFParameterInfo : : Finalize ( )
{
this - > complete_labels = true ;
for ( uint32 value = this - > min_value ; value < = this - > max_value ; value + + ) {
if ( ! this - > value_names . Contains ( value ) ) {
this - > complete_labels = false ;
break ;
}
}
}
2008-09-04 19:43:20 +00:00
/**
* Update the palettes of the graphics from the config file .
2011-05-03 20:30:26 +00:00
* Called when changing the default palette in advanced settings .
* @ param p1 Unused .
* @ return Always true .
2008-09-04 19:43:20 +00:00
*/
2011-05-03 20:30:26 +00:00
bool UpdateNewGRFConfigPalette ( int32 p1 )
2008-09-04 19:43:20 +00:00
{
2019-04-10 21:07:06 +00:00
for ( GRFConfig * c = _grfconfig_newgame ; c ! = nullptr ; c = c - > next ) c - > SetSuitablePalette ( ) ;
for ( GRFConfig * c = _grfconfig_static ; c ! = nullptr ; c = c - > next ) c - > SetSuitablePalette ( ) ;
for ( GRFConfig * c = _all_grfs ; c ! = nullptr ; c = c - > next ) c - > SetSuitablePalette ( ) ;
2011-05-03 20:30:26 +00:00
return true ;
2008-09-04 19:43:20 +00:00
}
2012-02-04 13:29:04 +00:00
/**
* Get the data section size of a GRF .
* @ param f GRF .
* @ return Size of the data section or SIZE_MAX if the file has no separate data section .
*/
size_t GRFGetSizeOfDataSection ( FILE * f )
{
extern const byte _grf_cont_v2_sig [ ] ;
static const uint header_len = 14 ;
byte data [ header_len ] ;
if ( fread ( data , 1 , header_len , f ) = = header_len ) {
if ( data [ 0 ] = = 0 & & data [ 1 ] = = 0 & & MemCmpT ( data + 2 , _grf_cont_v2_sig , 8 ) = = 0 ) {
/* Valid container version 2, get data section size. */
2013-11-23 18:11:01 +00:00
size_t offset = ( ( size_t ) data [ 13 ] < < 24 ) | ( ( size_t ) data [ 12 ] < < 16 ) | ( ( size_t ) data [ 11 ] < < 8 ) | ( size_t ) data [ 10 ] ;
if ( offset > = 1 * 1024 * 1024 * 1024 ) {
DEBUG ( grf , 0 , " Unexpectedly large offset for NewGRF " ) ;
/* Having more than 1 GiB of data is very implausible. Mostly because then
* all pools in OpenTTD are flooded already . Or it ' s just Action C all over .
* In any case , the offsets to graphics will likely not work either . */
return SIZE_MAX ;
}
2012-02-04 13:29:04 +00:00
return header_len + offset ;
}
}
return SIZE_MAX ;
}
2019-09-22 23:26:45 +00:00
struct GRFMD5SumState {
GRFConfig * config ;
size_t size ;
FILE * f ;
} ;
static uint _grf_md5_parallel = 0 ;
static uint _grf_md5_threads = 0 ;
static std : : mutex _grf_md5_lock ;
static std : : vector < GRFMD5SumState > _grf_md5_pending ;
static std : : condition_variable _grf_md5_full_cv ;
static std : : condition_variable _grf_md5_empty_cv ;
static std : : condition_variable _grf_md5_done_cv ;
2021-03-01 18:04:12 +00:00
static std : : atomic < bool > _abort_grf_scan ;
2019-09-22 23:26:45 +00:00
static const uint GRF_MD5_PENDING_MAX = 8 ;
static void CalcGRFMD5SumFromState ( const GRFMD5SumState & state )
{
Md5 checksum ;
uint8 buffer [ 1024 ] ;
size_t len ;
size_t size = state . size ;
while ( ( len = fread ( buffer , 1 , ( size > sizeof ( buffer ) ) ? sizeof ( buffer ) : size , state . f ) ) ! = 0 & & size ! = 0 ) {
size - = len ;
checksum . Append ( buffer , len ) ;
}
checksum . Finish ( state . config - > ident . md5sum ) ;
FioFCloseFile ( state . f ) ;
}
void CalcGRFMD5Thread ( )
{
std : : unique_lock < std : : mutex > lk ( _grf_md5_lock ) ;
while ( _grf_md5_parallel > 0 | | ! _grf_md5_pending . empty ( ) ) {
if ( _grf_md5_pending . empty ( ) ) {
_grf_md5_empty_cv . wait ( lk ) ;
} else {
const bool full = _grf_md5_pending . size ( ) = = GRF_MD5_PENDING_MAX ;
GRFMD5SumState state = _grf_md5_pending . back ( ) ;
_grf_md5_pending . pop_back ( ) ;
lk . unlock ( ) ;
if ( full ) _grf_md5_full_cv . notify_one ( ) ;
2021-03-01 18:04:12 +00:00
if ( ! _abort_grf_scan . load ( std : : memory_order_relaxed ) ) CalcGRFMD5SumFromState ( state ) ;
2019-09-22 23:26:45 +00:00
lk . lock ( ) ;
}
}
_grf_md5_threads - - ;
if ( _grf_md5_threads = = 0 ) {
_grf_md5_done_cv . notify_all ( ) ;
}
}
void CalcGRFMD5ThreadingStart ( )
{
_grf_md5_parallel = std : : thread : : hardware_concurrency ( ) ;
if ( _grf_md5_parallel < = 1 ) _grf_md5_parallel = 0 ;
}
void CalcGRFMD5ThreadingEnd ( )
{
if ( _grf_md5_parallel ) {
std : : unique_lock < std : : mutex > lk ( _grf_md5_lock ) ;
_grf_md5_parallel = 0 ;
_grf_md5_empty_cv . notify_all ( ) ;
_grf_md5_done_cv . wait ( lk , [ ] ( ) { return _grf_md5_threads = = 0 ; } ) ;
}
}
2010-08-01 19:22:34 +00:00
/**
* Calculate the MD5 sum for a GRF , and store it in the config .
2010-04-03 20:02:08 +00:00
* @ param config GRF to compute .
2011-08-24 13:48:29 +00:00
* @ param subdir The subdirectory to look in .
2010-04-03 20:02:08 +00:00
* @ return MD5 sum was successfully computed
*/
2011-08-24 13:48:29 +00:00
static bool CalcGRFMD5Sum ( GRFConfig * config , Subdirectory subdir )
2006-12-04 08:30:04 +00:00
{
2019-09-22 23:26:45 +00:00
size_t size ;
2006-12-04 08:30:04 +00:00
/* open the file */
2019-09-22 23:26:45 +00:00
FILE * f = FioFOpenFile ( config - > filename , " rb " , subdir , & size ) ;
2019-04-10 21:07:06 +00:00
if ( f = = nullptr ) return false ;
2006-12-04 08:30:04 +00:00
2013-11-23 13:17:45 +00:00
long start = ftell ( f ) ;
2021-01-08 10:16:18 +00:00
size = std : : min ( size , GRFGetSizeOfDataSection ( f ) ) ;
2013-11-23 13:15:07 +00:00
2013-11-23 13:17:45 +00:00
if ( start < 0 | | fseek ( f , start , SEEK_SET ) < 0 ) {
2013-11-23 13:15:07 +00:00
FioFCloseFile ( f ) ;
return false ;
}
2012-02-04 13:29:04 +00:00
2006-12-04 08:30:04 +00:00
/* calculate md5sum */
2019-09-22 23:26:45 +00:00
GRFMD5SumState state { config , size , f } ;
if ( _grf_md5_parallel = = 0 ) {
CalcGRFMD5SumFromState ( state ) ;
return true ;
2006-12-04 08:30:04 +00:00
}
2019-09-22 23:26:45 +00:00
std : : unique_lock < std : : mutex > lk ( _grf_md5_lock ) ;
if ( _grf_md5_pending . size ( ) > = GRF_MD5_PENDING_MAX ) {
_grf_md5_full_cv . wait ( lk ) ;
}
_grf_md5_pending . push_back ( state ) ;
bool notify_reader = ( _grf_md5_pending . size ( ) = = 1 ) ; // queue was empty
if ( ( _grf_md5_threads = = 0 ) | | ( ( _grf_md5_pending . size ( ) > 1 ) & & ( _grf_md5_threads < _grf_md5_parallel ) ) ) {
_grf_md5_threads + + ;
if ( ! StartNewThread ( nullptr , " ottd:grf-md5 " , & CalcGRFMD5Thread ) ) {
_grf_md5_parallel = 0 ;
lk . unlock ( ) ;
CalcGRFMD5Thread ( ) ;
return true ;
}
}
lk . unlock ( ) ;
if ( notify_reader ) _grf_md5_empty_cv . notify_one ( ) ;
2006-12-04 08:30:04 +00:00
return true ;
}
2010-08-01 19:22:34 +00:00
/**
* Find the GRFID of a given grf , and calculate its md5sum .
2010-04-03 20:02:08 +00:00
* @ param config grf to fill .
* @ param is_static grf is static .
2011-08-24 13:48:29 +00:00
* @ param subdir the subdirectory to search in .
2010-04-03 20:02:08 +00:00
* @ return Operation was successfully completed .
*/
2011-08-24 13:48:29 +00:00
bool FillGRFDetails ( GRFConfig * config , bool is_static , Subdirectory subdir )
2006-12-04 08:30:04 +00:00
{
2011-08-24 13:48:29 +00:00
if ( ! FioCheckFileExists ( config - > filename , subdir ) ) {
2007-03-06 19:33:28 +00:00
config - > status = GCS_NOT_FOUND ;
2006-12-04 08:30:04 +00:00
return false ;
}
/* Find and load the Action 8 information */
2011-08-24 13:48:29 +00:00
LoadNewGRFFile ( config , CONFIG_SLOT , GLS_FILESCAN , subdir ) ;
2010-08-02 16:04:24 +00:00
config - > SetSuitablePalette ( ) ;
2012-06-01 15:20:18 +00:00
config - > FinalizeParameterInfo ( ) ;
2006-12-04 08:30:04 +00:00
2017-01-07 21:22:26 +00:00
/* Skip if the grfid is 0 (not read) or if it is an internal GRF */
if ( config - > ident . grfid = = 0 | | HasBit ( config - > flags , GCF_SYSTEM ) ) return false ;
2006-12-04 08:30:04 +00:00
2007-01-03 11:33:54 +00:00
if ( is_static ) {
/* Perform a 'safety scan' for static GRFs */
2017-01-07 21:18:17 +00:00
LoadNewGRFFile ( config , CONFIG_SLOT , GLS_SAFETYSCAN , subdir ) ;
2007-01-03 11:33:54 +00:00
/* GCF_UNSAFE is set if GLS_SAFETYSCAN finds unsafe actions */
2007-11-19 21:02:30 +00:00
if ( HasBit ( config - > flags , GCF_UNSAFE ) ) return false ;
2007-01-03 11:33:54 +00:00
}
2011-08-24 13:48:29 +00:00
return CalcGRFMD5Sum ( config , subdir ) ;
2006-12-04 08:30:04 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Clear a GRF Config list , freeing all nodes .
2010-04-03 20:02:08 +00:00
* @ param config Start of the list .
2019-04-10 21:07:06 +00:00
* @ post \ a config is set to \ c nullptr .
2010-04-03 20:02:08 +00:00
*/
2006-12-20 21:17:33 +00:00
void ClearGRFConfigList ( GRFConfig * * config )
2006-12-04 08:30:04 +00:00
{
GRFConfig * c , * next ;
2019-04-10 21:07:06 +00:00
for ( c = * config ; c ! = nullptr ; c = next ) {
2006-12-04 08:30:04 +00:00
next = c - > next ;
2010-02-25 20:06:11 +00:00
delete c ;
2006-12-04 08:30:04 +00:00
}
2019-04-10 21:07:06 +00:00
* config = nullptr ;
2006-12-04 08:30:04 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Copy a GRF Config list
2006-12-20 21:17:33 +00:00
* @ param dst pointer to destination list
2007-02-18 22:37:33 +00:00
* @ param src pointer to source list values
2007-06-18 23:00:55 +00:00
* @ param init_only the copied GRF will be processed up to GLS_INIT
2010-08-01 19:44:49 +00:00
* @ return pointer to the last value added to the destination list
*/
2007-06-18 23:00:55 +00:00
GRFConfig * * CopyGRFConfigList ( GRFConfig * * dst , const GRFConfig * src , bool init_only )
2006-12-04 08:30:04 +00:00
{
2006-12-21 10:09:43 +00:00
/* Clear destination as it will be overwritten */
ClearGRFConfigList ( dst ) ;
2019-04-10 21:07:06 +00:00
for ( ; src ! = nullptr ; src = src - > next ) {
2010-07-31 09:32:44 +00:00
GRFConfig * c = new GRFConfig ( * src ) ;
2009-09-17 21:45:47 +00:00
2007-11-19 21:32:20 +00:00
ClrBit ( c - > flags , GCF_INIT_ONLY ) ;
2007-11-20 13:35:54 +00:00
if ( init_only ) SetBit ( c - > flags , GCF_INIT_ONLY ) ;
2007-06-18 23:00:55 +00:00
2006-12-04 08:30:04 +00:00
* dst = c ;
dst = & c - > next ;
}
2006-12-12 19:38:41 +00:00
return dst ;
2006-12-04 08:30:04 +00:00
}
2006-12-27 18:25:17 +00:00
/**
* Removes duplicates from lists of GRFConfigs . These duplicates
* are introduced when the _grfconfig_static GRFs are appended
* to the _grfconfig on a newgame or savegame . As the parameters
* of the static GRFs could be different that the parameters of
* the ones used non - statically . This can result in desyncs in
* multiplayers , so the duplicate static GRFs have to be removed .
*
* This function _assumes_ that all static GRFs are placed after
* the non - static GRFs .
*
* @ param list the list to remove the duplicates from
*/
static void RemoveDuplicatesFromGRFConfigList ( GRFConfig * list )
{
GRFConfig * prev ;
GRFConfig * cur ;
2019-04-10 21:07:06 +00:00
if ( list = = nullptr ) return ;
2006-12-27 18:25:17 +00:00
2019-04-10 21:07:06 +00:00
for ( prev = list , cur = list - > next ; cur ! = nullptr ; prev = cur , cur = cur - > next ) {
2010-02-25 20:05:31 +00:00
if ( cur - > ident . grfid ! = list - > ident . grfid ) continue ;
2007-01-14 22:43:19 +00:00
2006-12-27 18:25:17 +00:00
prev - > next = cur - > next ;
2010-02-25 20:06:11 +00:00
delete cur ;
2006-12-27 18:25:17 +00:00
cur = prev ; // Just go back one so it continues as normal later on
}
RemoveDuplicatesFromGRFConfigList ( list - > next ) ;
}
/**
* Appends the static GRFs to a list of GRFs
* @ param dst the head of the list to add to
*/
void AppendStaticGRFConfigs ( GRFConfig * * dst )
{
GRFConfig * * tail = dst ;
2019-04-10 21:07:06 +00:00
while ( * tail ! = nullptr ) tail = & ( * tail ) - > next ;
2006-12-27 18:25:17 +00:00
2007-06-18 23:00:55 +00:00
CopyGRFConfigList ( tail , _grfconfig_static , false ) ;
2006-12-27 18:25:17 +00:00
RemoveDuplicatesFromGRFConfigList ( * dst ) ;
}
2010-08-01 19:22:34 +00:00
/**
* Appends an element to a list of GRFs
2007-04-17 20:23:13 +00:00
* @ param dst the head of the list to add to
2010-08-01 19:44:49 +00:00
* @ param el the new tail to be
*/
2007-02-22 16:16:44 +00:00
void AppendToGRFConfigList ( GRFConfig * * dst , GRFConfig * el )
2007-02-18 22:37:33 +00:00
{
GRFConfig * * tail = dst ;
2019-04-10 21:07:06 +00:00
while ( * tail ! = nullptr ) tail = & ( * tail ) - > next ;
2007-02-22 16:16:44 +00:00
* tail = el ;
2007-02-18 22:37:33 +00:00
RemoveDuplicatesFromGRFConfigList ( * dst ) ;
}
2006-12-04 08:30:04 +00:00
2010-04-03 20:02:08 +00:00
/** Reset the current GRF Config to either blank or newgame settings. */
2006-12-04 08:30:04 +00:00
void ResetGRFConfig ( bool defaults )
{
2007-06-18 23:00:55 +00:00
CopyGRFConfigList ( & _grfconfig , _grfconfig_newgame , ! defaults ) ;
2006-12-27 18:25:17 +00:00
AppendStaticGRFConfigs ( & _grfconfig ) ;
2006-12-04 08:30:04 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Check if all GRFs in the GRF config from a savegame can be loaded .
2010-06-13 14:07:49 +00:00
* @ param grfconfig GrfConfig to check
2007-01-13 17:42:50 +00:00
* @ return will return any of the following 3 values : < br >
* < ul >
2010-07-31 21:02:56 +00:00
* < li > GLC_ALL_GOOD : No problems occurred , all GRF files were found and loaded
2007-03-06 19:33:28 +00:00
* < li > GLC_COMPATIBLE : For one or more GRF ' s no exact match was found , but a
2007-01-13 17:42:50 +00:00
* compatible GRF with the same grfid was found and used instead
2007-03-06 19:33:28 +00:00
* < li > GLC_NOT_FOUND : For one or more GRF ' s no match was found at all
2010-08-01 19:44:49 +00:00
* < / ul >
*/
2010-06-13 14:07:49 +00:00
GRFListCompatibility IsGoodGRFConfigList ( GRFConfig * grfconfig )
2006-12-04 08:30:04 +00:00
{
2007-03-06 19:33:28 +00:00
GRFListCompatibility res = GLC_ALL_GOOD ;
2006-12-04 08:30:04 +00:00
2019-04-10 21:07:06 +00:00
for ( GRFConfig * c = grfconfig ; c ! = nullptr ; c = c - > next ) {
2010-10-17 12:12:13 +00:00
const GRFConfig * f = FindGRFConfig ( c - > ident . grfid , FGCM_EXACT , c - > ident . md5sum ) ;
2019-04-10 21:07:06 +00:00
if ( f = = nullptr | | HasBit ( f - > flags , GCF_INVALID ) ) {
2007-01-13 21:28:46 +00:00
char buf [ 256 ] ;
2007-01-13 17:42:50 +00:00
/* If we have not found the exactly matching GRF try to find one with the
* same grfid , as it most likely is compatible */
2019-04-10 21:07:06 +00:00
f = FindGRFConfig ( c - > ident . grfid , FGCM_COMPATIBLE , nullptr , c - > version ) ;
if ( f ! = nullptr ) {
2010-02-25 20:05:31 +00:00
md5sumToString ( buf , lastof ( buf ) , c - > ident . md5sum ) ;
2020-12-16 02:06:48 +00:00
DEBUG ( grf , 1 , " NewGRF %08X (%s) not found; checksum %s, name: '%s'. Compatibility mode on " , BSWAP32 ( c - > ident . grfid ) , c - > GetDisplayPath ( ) , buf , GetDefaultLangGRFStringFromGRFText ( c - > name ) ) ;
2010-06-13 14:11:32 +00:00
if ( ! HasBit ( c - > flags , GCF_COMPATIBLE ) ) {
/* Preserve original_md5sum after it has been assigned */
SetBit ( c - > flags , GCF_COMPATIBLE ) ;
memcpy ( c - > original_md5sum , c - > ident . md5sum , sizeof ( c - > original_md5sum ) ) ;
}
2007-01-13 17:42:50 +00:00
/* Non-found has precedence over compatibility load */
2007-03-06 19:33:28 +00:00
if ( res ! = GLC_NOT_FOUND ) res = GLC_COMPATIBLE ;
2007-01-13 17:42:50 +00:00
goto compatible_grf ;
}
2006-12-04 08:30:04 +00:00
2007-01-13 17:42:50 +00:00
/* No compatible grf was found, mark it as disabled */
2010-02-25 20:05:31 +00:00
md5sumToString ( buf , lastof ( buf ) , c - > ident . md5sum ) ;
2020-12-16 02:06:48 +00:00
DEBUG ( grf , 0 , " NewGRF %08X (%s) not found; checksum %s, name: '%s' " , BSWAP32 ( c - > ident . grfid ) , c - > GetDisplayPath ( ) , buf , GetDefaultLangGRFStringFromGRFText ( c - > name ) ) ;
2006-12-04 08:30:04 +00:00
2007-03-06 19:33:28 +00:00
c - > status = GCS_NOT_FOUND ;
res = GLC_NOT_FOUND ;
2006-12-04 08:30:04 +00:00
} else {
2007-01-13 17:42:50 +00:00
compatible_grf :
2019-12-11 20:34:04 +00:00
DEBUG ( grf , 1 , " Loading GRF %08X from %s " , BSWAP32 ( f - > ident . grfid ) , f - > GetDisplayPath ( ) ) ;
2006-12-20 23:44:39 +00:00
/* The filename could be the filename as in the savegame. As we need
* to load the GRF here , we need the correct filename , so overwrite that
* in any case and set the name and info when it is not set already .
* When the GCF_COPY flag is set , it is certain that the filename is
* already a local one , so there is no need to replace it . */
2007-11-19 21:02:30 +00:00
if ( ! HasBit ( c - > flags , GCF_COPY ) ) {
2006-12-20 23:44:39 +00:00
free ( c - > filename ) ;
2014-04-25 15:40:32 +00:00
c - > filename = stredup ( f - > filename ) ;
2010-02-25 20:05:31 +00:00
memcpy ( c - > ident . md5sum , f - > ident . md5sum , sizeof ( c - > ident . md5sum ) ) ;
2011-03-03 18:47:46 +00:00
c - > name = f - > name ;
c - > info = f - > name ;
2019-04-10 21:07:06 +00:00
c - > error = nullptr ;
2010-09-08 15:35:54 +00:00
c - > version = f - > version ;
2010-10-17 12:12:52 +00:00
c - > min_loadable_version = f - > min_loadable_version ;
2010-09-08 15:35:54 +00:00
c - > num_valid_params = f - > num_valid_params ;
c - > has_param_defaults = f - > has_param_defaults ;
2018-09-23 11:23:54 +00:00
for ( uint i = 0 ; i < f - > param_info . size ( ) ; i + + ) {
2019-04-10 21:07:06 +00:00
if ( f - > param_info [ i ] = = nullptr ) {
c - > param_info . push_back ( nullptr ) ;
2010-09-08 15:35:54 +00:00
} else {
2019-02-18 22:39:06 +00:00
c - > param_info . push_back ( new GRFParameterInfo ( * f - > param_info [ i ] ) ) ;
2010-09-08 15:35:54 +00:00
}
}
2006-12-20 23:44:39 +00:00
}
2006-12-04 08:30:04 +00:00
}
}
return res ;
}
2008-08-31 08:38:30 +00:00
/** Helper for scanning for files with GRF as extension */
class GRFFileScanner : FileScanner {
2021-02-19 18:31:45 +00:00
std : : chrono : : steady_clock : : time_point next_update ; ///< The next moment we do update the screen.
2011-08-21 12:53:00 +00:00
uint num_scanned ; ///< The number of GRFs we have scanned.
2019-09-22 23:26:45 +00:00
std : : vector < GRFConfig * > grfs ;
2011-08-21 12:53:00 +00:00
2008-08-31 08:38:30 +00:00
public :
2020-04-11 17:23:38 +00:00
GRFFileScanner ( ) : num_scanned ( 0 )
2011-08-21 12:53:00 +00:00
{
2021-02-19 18:31:45 +00:00
this - > next_update = std : : chrono : : steady_clock : : now ( ) ;
2011-08-21 12:53:00 +00:00
}
2020-12-06 20:11:50 +00:00
bool AddFile ( const std : : string & filename , size_t basepath_length , const std : : string & tar_filename ) override ;
2008-08-31 08:38:30 +00:00
/** Do the scan for GRFs. */
static uint DoScan ( )
{
2019-09-22 23:26:45 +00:00
CalcGRFMD5ThreadingStart ( ) ;
2008-08-31 08:38:30 +00:00
GRFFileScanner fs ;
2019-09-22 23:26:45 +00:00
fs . grfs . clear ( ) ;
2011-08-24 13:33:49 +00:00
int ret = fs . Scan ( " .grf " , NEWGRF_DIR ) ;
2019-09-22 23:26:45 +00:00
CalcGRFMD5ThreadingEnd ( ) ;
for ( GRFConfig * c : fs . grfs ) {
bool added = true ;
if ( _all_grfs = = nullptr ) {
_all_grfs = c ;
} else {
/* Insert file into list at a position determined by its
* name , so the list is sorted as we go along */
GRFConfig * * pd , * d ;
bool stop = false ;
for ( pd = & _all_grfs ; ( d = * pd ) ! = nullptr ; pd = & d - > next ) {
if ( c - > ident . grfid = = d - > ident . grfid & & memcmp ( c - > ident . md5sum , d - > ident . md5sum , sizeof ( c - > ident . md5sum ) ) = = 0 ) added = false ;
/* Because there can be multiple grfs with the same name, make sure we checked all grfs with the same name,
* before inserting the entry . So insert a new grf at the end of all grfs with the same name , instead of
* just after the first with the same name . Avoids doubles in the list . */
if ( strcasecmp ( c - > GetName ( ) , d - > GetName ( ) ) < = 0 ) {
stop = true ;
} else if ( stop ) {
break ;
}
}
if ( added ) {
c - > next = d ;
* pd = c ;
} else {
delete c ;
ret - - ;
}
}
}
2011-08-21 12:53:13 +00:00
/* The number scanned and the number returned may not be the same;
* duplicate NewGRFs and base sets are ignored in the return value . */
_settings_client . gui . last_newgrf_count = fs . num_scanned ;
return ret ;
2008-08-31 08:38:30 +00:00
}
} ;
2020-12-06 20:11:50 +00:00
bool GRFFileScanner : : AddFile ( const std : : string & filename , size_t basepath_length , const std : : string & tar_filename )
2007-09-13 18:28:56 +00:00
{
2021-03-01 18:04:12 +00:00
if ( _abort_grf_scan . load ( std : : memory_order_relaxed ) ) return false ;
2020-12-06 20:11:50 +00:00
GRFConfig * c = new GRFConfig ( filename . c_str ( ) + basepath_length ) ;
2007-09-13 18:28:56 +00:00
2019-09-22 23:26:45 +00:00
bool added = FillGRFDetails ( c , false ) ;
if ( added ) {
this - > grfs . push_back ( c ) ;
2007-09-13 18:28:56 +00:00
}
2006-12-04 08:30:04 +00:00
2011-08-21 12:53:00 +00:00
this - > num_scanned + + ;
2021-02-19 20:34:14 +00:00
const auto now = std : : chrono : : steady_clock : : now ( ) ;
if ( now > = this - > next_update ) {
this - > next_update = now + std : : chrono : : milliseconds ( MODAL_PROGRESS_REDRAW_TIMEOUT ) ;
2021-02-19 18:31:45 +00:00
2019-03-10 23:45:39 +00:00
_modal_progress_work_mutex . unlock ( ) ;
_modal_progress_paint_mutex . lock ( ) ;
2011-08-21 12:53:00 +00:00
2019-04-10 21:07:06 +00:00
const char * name = nullptr ;
2020-05-17 21:31:47 +00:00
if ( c - > name ! = nullptr ) name = GetGRFStringFromGRFText ( c - > name ) ;
2019-04-10 21:07:06 +00:00
if ( name = = nullptr ) name = c - > filename ;
2011-08-21 12:53:13 +00:00
UpdateNewGRFScanStatus ( this - > num_scanned , name ) ;
2011-08-21 12:53:00 +00:00
2019-03-10 23:45:39 +00:00
_modal_progress_work_mutex . lock ( ) ;
_modal_progress_paint_mutex . unlock ( ) ;
2011-08-21 12:53:00 +00:00
}
2019-11-03 17:56:45 +00:00
if ( ! added ) {
/* File couldn't be opened, or is either not a NewGRF or is a
* ' system ' NewGRF or it ' s already known , so forget about it . */
delete c ;
}
2007-09-13 18:28:56 +00:00
return added ;
}
2006-12-04 08:30:04 +00:00
2007-09-27 20:39:46 +00:00
/**
* Simple sorter for GRFS
2019-04-13 21:11:56 +00:00
* @ param c1 the first GRFConfig *
* @ param c2 the second GRFConfig *
* @ return true if the name of first NewGRF is before the name of the second .
2007-09-27 20:39:46 +00:00
*/
2019-04-13 21:11:56 +00:00
static bool GRFSorter ( GRFConfig * const & c1 , GRFConfig * const & c2 )
2007-09-27 20:39:46 +00:00
{
2019-04-13 21:11:56 +00:00
return strnatcmp ( c1 - > GetName ( ) , c2 - > GetName ( ) ) < 0 ;
2007-09-27 20:39:46 +00:00
}
2011-08-21 12:49:51 +00:00
/**
2011-08-21 12:53:00 +00:00
* Really perform the scan for all NewGRFs .
2011-08-21 12:49:51 +00:00
* @ param callback The callback to call after the scanning is complete .
*/
2019-03-17 00:59:46 +00:00
void DoScanNewGRFFiles ( NewGRFScanCallback * callback )
2006-12-04 08:30:04 +00:00
{
2019-03-10 23:45:39 +00:00
std : : unique_lock < std : : mutex > lock_work ( _modal_progress_work_mutex ) ;
2011-08-21 12:53:00 +00:00
2006-12-20 21:17:33 +00:00
ClearGRFConfigList ( & _all_grfs ) ;
2011-11-14 21:40:39 +00:00
TarScanner : : DoScan ( TarScanner : : NEWGRF ) ;
2006-12-04 08:30:04 +00:00
2006-12-26 17:36:18 +00:00
DEBUG ( grf , 1 , " Scanning for NewGRFs " ) ;
2008-08-31 08:38:30 +00:00
uint num = GRFFileScanner : : DoScan ( ) ;
2007-09-27 20:39:46 +00:00
2006-12-26 17:36:18 +00:00
DEBUG ( grf , 1 , " Scan complete, found %d files " , num ) ;
2019-04-10 21:07:06 +00:00
if ( num ! = 0 & & _all_grfs ! = nullptr ) {
2011-08-20 17:46:03 +00:00
/* Sort the linked list using quicksort.
2014-10-12 18:41:53 +00:00
* For that we first have to make an array , then sort and
* then remake the linked list . */
2019-04-13 21:11:56 +00:00
std : : vector < GRFConfig * > to_sort ;
2011-08-20 17:46:03 +00:00
uint i = 0 ;
2019-04-10 21:07:06 +00:00
for ( GRFConfig * p = _all_grfs ; p ! = nullptr ; p = p - > next , i + + ) {
2019-04-13 21:11:56 +00:00
to_sort . push_back ( p ) ;
2011-08-20 17:46:03 +00:00
}
/* Number of files is not necessarily right */
num = i ;
2007-09-27 20:39:46 +00:00
2019-04-13 21:11:56 +00:00
std : : sort ( to_sort . begin ( ) , to_sort . end ( ) , GRFSorter ) ;
2007-09-27 20:39:46 +00:00
2011-08-20 17:46:03 +00:00
for ( i = 1 ; i < num ; i + + ) {
to_sort [ i - 1 ] - > next = to_sort [ i ] ;
}
2019-04-10 21:07:06 +00:00
to_sort [ num - 1 ] - > next = nullptr ;
2011-08-20 17:46:03 +00:00
_all_grfs = to_sort [ 0 ] ;
2008-05-07 17:36:28 +00:00
2011-08-20 17:46:03 +00:00
NetworkAfterNewGRFScan ( ) ;
}
2019-03-10 23:45:39 +00:00
lock_work . unlock ( ) ;
std : : lock_guard < std : : mutex > lock_paint ( _modal_progress_paint_mutex ) ;
2011-08-21 12:53:00 +00:00
2011-08-20 17:46:03 +00:00
/* Yes... these are the NewGRF windows */
2011-08-21 12:53:00 +00:00
InvalidateWindowClassesData ( WC_SAVELOAD , 0 , true ) ;
2011-12-19 20:50:21 +00:00
InvalidateWindowData ( WC_GAME_OPTIONS , WN_GAME_OPTIONS_NEWGRF_STATE , GOID_NEWGRF_RESCANNED , true ) ;
2019-04-10 21:07:06 +00:00
if ( callback ! = nullptr ) callback - > OnNewGRFsScanned ( ) ;
2011-08-21 12:53:00 +00:00
DeleteWindowByClass ( WC_MODAL_PROGRESS ) ;
SetModalProgress ( false ) ;
MarkWholeScreenDirty ( ) ;
2006-12-04 08:30:04 +00:00
}
2011-08-21 12:53:00 +00:00
/**
* Scan for all NewGRFs .
* @ param callback The callback to call after the scanning is complete .
*/
void ScanNewGRFFiles ( NewGRFScanCallback * callback )
{
2011-08-24 12:18:53 +00:00
/* First set the modal progress. This ensures that it will eventually let go of the paint mutex. */
SetModalProgress ( true ) ;
/* Only then can we really start, especially by marking the whole screen dirty. Get those other windows hidden!. */
MarkWholeScreenDirty ( ) ;
2019-04-10 21:07:06 +00:00
if ( ! UseThreadedModelProgress ( ) | | ! VideoDriver : : GetInstance ( ) - > HasGUI ( ) | | ! StartNewThread ( nullptr , " ottd:newgrf-scan " , & DoScanNewGRFFiles , ( NewGRFScanCallback * ) callback ) ) { // Without the seemingly superfluous cast, strange compiler errors ensue.
2019-03-10 23:45:39 +00:00
_modal_progress_work_mutex . unlock ( ) ;
_modal_progress_paint_mutex . unlock ( ) ;
2011-08-21 12:53:00 +00:00
DoScanNewGRFFiles ( callback ) ;
2019-03-10 23:45:39 +00:00
_modal_progress_paint_mutex . lock ( ) ;
_modal_progress_work_mutex . lock ( ) ;
2011-08-24 12:18:53 +00:00
} else {
2019-04-10 21:07:06 +00:00
UpdateNewGRFScanStatus ( 0 , nullptr ) ;
2011-08-21 12:53:00 +00:00
}
}
2006-12-04 08:30:04 +00:00
2021-03-01 18:04:12 +00:00
void AbortScanNewGRFFiles ( )
{
_abort_grf_scan . store ( true , std : : memory_order_relaxed ) ;
}
2010-08-01 19:22:34 +00:00
/**
* Find a NewGRF in the scanned list .
2010-04-03 20:02:08 +00:00
* @ param grfid GRFID to look for ,
2010-10-17 12:12:13 +00:00
* @ param mode Restrictions for matching grfs
* @ param md5sum Expected MD5 sum
2010-10-17 12:12:52 +00:00
* @ param desired_version Requested version
2019-04-10 21:07:06 +00:00
* @ return The matching grf , if it exists in # _all_grfs , else \ c nullptr .
2010-04-03 20:02:08 +00:00
*/
2010-10-17 12:12:52 +00:00
const GRFConfig * FindGRFConfig ( uint32 grfid , FindGRFConfigMode mode , const uint8 * md5sum , uint32 desired_version )
2006-12-04 08:30:04 +00:00
{
2019-04-10 21:07:06 +00:00
assert ( ( mode = = FGCM_EXACT ) ! = ( md5sum = = nullptr ) ) ;
const GRFConfig * best = nullptr ;
for ( const GRFConfig * c = _all_grfs ; c ! = nullptr ; c = c - > next ) {
2010-10-17 12:12:13 +00:00
/* if md5sum is set, we look for an exact match and continue if not found */
2010-07-31 14:40:50 +00:00
if ( ! c - > ident . HasGrfIdentifier ( grfid , md5sum ) ) continue ;
2010-10-17 12:12:13 +00:00
/* return it, if the exact same newgrf is found, or if we do not care about finding "the best" */
2019-04-10 21:07:06 +00:00
if ( md5sum ! = nullptr | | mode = = FGCM_ANY ) return c ;
2011-01-15 21:13:47 +00:00
/* Skip incompatible stuff, unless explicitly allowed */
if ( mode ! = FGCM_NEWEST & & HasBit ( c - > flags , GCF_INVALID ) ) continue ;
2010-10-17 12:12:52 +00:00
/* check version compatibility */
if ( mode = = FGCM_COMPATIBLE & & ( c - > version < desired_version | | c - > min_loadable_version > desired_version ) ) continue ;
2010-10-17 12:12:13 +00:00
/* remember the newest one as "the best" */
2019-04-10 21:07:06 +00:00
if ( best = = nullptr | | c - > version > best - > version ) best = c ;
2006-12-04 08:30:04 +00:00
}
2010-07-31 14:40:50 +00:00
return best ;
2006-12-04 08:30:04 +00:00
}
2006-12-18 12:26:55 +00:00
/** Structure for UnknownGRFs; this is a lightweight variant of GRFConfig */
2007-01-21 17:29:38 +00:00
struct UnknownGRF : public GRFIdentifier {
2020-05-17 21:31:47 +00:00
GRFTextWrapper name ; ///< Name of the GRF.
UnknownGRF ( ) = default ;
UnknownGRF ( const UnknownGRF & other ) = default ;
UnknownGRF ( UnknownGRF & & other ) = default ;
UnknownGRF ( uint32 grfid , const uint8 * _md5sum ) : GRFIdentifier ( grfid , _md5sum ) , name ( new GRFTextList ) { }
2006-12-18 12:26:55 +00:00
} ;
/**
* Finds the name of a NewGRF in the list of names for unknown GRFs . An
* unknown GRF is a GRF where the . grf is not found during scanning .
*
* The names are resolved via UDP calls to servers that should know the name ,
* though the replies may not come . This leaves " <Unknown> " as name , though
* that shouldn ' t matter _very_ much as they need GRF crawler or so to look
* up the GRF anyway and that works better with the GRF ID .
*
* @ param grfid the GRF ID part of the ' unique ' GRF identifier
* @ param md5sum the MD5 checksum part of the ' unique ' GRF identifier
* @ param create whether to create a new GRFConfig if the GRFConfig did not
* exist in the fake list of GRFConfigs .
2011-03-03 18:47:46 +00:00
* @ return The GRFTextWrapper of the name of the GRFConfig with the given GRF ID
2019-04-10 21:07:06 +00:00
* and MD5 checksum or nullptr when it does not exist and create is false .
2011-03-03 18:47:46 +00:00
* This value must NEVER be freed by the caller .
2006-12-18 12:26:55 +00:00
*/
2020-05-17 21:31:47 +00:00
GRFTextWrapper FindUnknownGRFName ( uint32 grfid , uint8 * md5sum , bool create )
2006-12-18 12:26:55 +00:00
{
2020-05-17 21:31:47 +00:00
static std : : vector < UnknownGRF > unknown_grfs ;
2006-12-18 12:26:55 +00:00
2020-05-17 21:31:47 +00:00
for ( const auto & grf : unknown_grfs ) {
if ( grf . grfid = = grfid ) {
if ( memcmp ( md5sum , grf . md5sum , sizeof ( grf . md5sum ) ) = = 0 ) return grf . name ;
2006-12-18 12:26:55 +00:00
}
}
2019-04-10 21:07:06 +00:00
if ( ! create ) return nullptr ;
2006-12-18 12:26:55 +00:00
2020-05-17 21:31:47 +00:00
unknown_grfs . emplace_back ( grfid , md5sum ) ;
UnknownGRF & grf = unknown_grfs . back ( ) ;
2011-03-03 18:47:46 +00:00
2020-05-17 21:31:47 +00:00
AddGRFTextToList ( grf . name , UNKNOWN_GRF_NAME_PLACEHOLDER ) ;
2006-12-18 12:26:55 +00:00
2020-05-17 21:31:47 +00:00
return grf . name ;
2006-12-18 12:26:55 +00:00
}
2010-08-01 19:22:34 +00:00
/**
* Retrieve a NewGRF from the current config by its grfid .
2010-04-03 20:02:08 +00:00
* @ param grfid grf to look for .
* @ param mask GRFID mask to allow for partial matching .
2019-04-10 21:07:06 +00:00
* @ return The grf config , if it exists , else \ c nullptr .
2010-04-03 20:02:08 +00:00
*/
2009-01-16 19:53:52 +00:00
GRFConfig * GetGRFConfig ( uint32 grfid , uint32 mask )
2006-12-04 13:45:20 +00:00
{
GRFConfig * c ;
2019-04-10 21:07:06 +00:00
for ( c = _grfconfig ; c ! = nullptr ; c = c - > next ) {
2010-02-25 20:05:31 +00:00
if ( ( c - > ident . grfid & mask ) = = ( grfid & mask ) ) return c ;
2006-12-04 13:45:20 +00:00
}
2019-04-10 21:07:06 +00:00
return nullptr ;
2006-12-04 13:45:20 +00:00
}
2010-04-03 20:02:08 +00:00
/** Build a string containing space separated parameter values, and terminate */
2006-12-10 11:29:14 +00:00
char * GRFBuildParamList ( char * dst , const GRFConfig * c , const char * last )
{
uint i ;
/* Return an empty string if there are no parameters */
if ( c - > num_params = = 0 ) return strecpy ( dst , " " , last ) ;
for ( i = 0 ; i < c - > num_params ; i + + ) {
if ( i > 0 ) dst = strecpy ( dst , " " , last ) ;
2008-10-28 14:42:31 +00:00
dst + = seprintf ( dst , last , " %d " , c - > param [ i ] ) ;
2006-12-10 11:29:14 +00:00
}
return dst ;
}
2011-11-10 06:15:03 +00:00
/**
2011-11-18 13:05:55 +00:00
* Search a textfile file next to this NewGRF .
2011-11-18 14:40:57 +00:00
* @ param type The type of the textfile to search for .
2019-04-10 21:07:06 +00:00
* @ return The filename for the textfile , \ c nullptr otherwise .
2011-11-10 06:15:03 +00:00
*/
2011-11-18 14:40:57 +00:00
const char * GRFConfig : : GetTextfile ( TextfileType type ) const
2011-11-10 06:15:03 +00:00
{
2012-02-12 10:51:43 +00:00
return : : GetTextfile ( type , NEWGRF_DIR , this - > filename ) ;
2011-11-10 06:15:03 +00:00
}