OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

newgrf_config.cpp
Go to the documentation of this file.
1 /* $Id: newgrf_config.cpp 27732 2017-01-14 18:30:26Z frosch $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
9
12 #include "stdafx.h"
13 #include "debug.h"
14 #include "3rdparty/md5/md5.h"
15 #include "newgrf.h"
16 #include "network/network_func.h"
17 #include "gfx_func.h"
18 #include "newgrf_text.h"
19 #include "window_func.h"
20 #include "progress.h"
21 #include "video/video_driver.hpp"
22 #include "strings_func.h"
23 #include "textfile_gui.h"
24
25 #include "fileio_func.h"
26 #include "fios.h"
27
28 #include "safeguards.h"
29
31 GRFTextWrapper::GRFTextWrapper() :
32 text(NULL)
33 {
34 }
35
37 GRFTextWrapper::~GRFTextWrapper()
38 {
39 CleanUpGRFText(this->text);
40 }
41
47 GRFConfig::GRFConfig(const char *filename) :
48 name(new GRFTextWrapper()),
49 info(new GRFTextWrapper()),
50 url(new GRFTextWrapper()),
51 num_valid_params(lengthof(param))
52 {
53 if (filename != NULL) this->filename = stredup(filename);
54 this->name->AddRef();
55 this->info->AddRef();
56 this->url->AddRef();
57 }
58
63 GRFConfig::GRFConfig(const GRFConfig &config) :
64 ZeroedMemoryAllocator(),
65 ident(config.ident),
66 name(config.name),
67 info(config.info),
68 url(config.url),
69 version(config.version),
70 min_loadable_version(config.min_loadable_version),
71 flags(config.flags & ~(1 << GCF_COPY)),
72 status(config.status),
73 grf_bugs(config.grf_bugs),
74 num_params(config.num_params),
75 num_valid_params(config.num_valid_params),
76 palette(config.palette),
77 has_param_defaults(config.has_param_defaults)
78 {
79 MemCpyT<uint8>(this->original_md5sum, config.original_md5sum, lengthof(this->original_md5sum));
80 MemCpyT<uint32>(this->param, config.param, lengthof(this->param));
81 if (config.filename != NULL) this->filename = stredup(config.filename);
82 this->name->AddRef();
83 this->info->AddRef();
84 this->url->AddRef();
85 if (config.error != NULL) this->error = new GRFError(*config.error);
86 for (uint i = 0; i < config.param_info.Length(); i++) {
87 if (config.param_info[i] == NULL) {
88 *this->param_info.Append() = NULL;
89 } else {
90 *this->param_info.Append() = new GRFParameterInfo(*config.param_info[i]);
91 }
92 }
93 }
94
96 GRFConfig::~GRFConfig()
97 {
98 /* GCF_COPY as in NOT stredupped/alloced the filename */
99 if (!HasBit(this->flags, GCF_COPY)) {
100 free(this->filename);
101 delete this->error;
102 }
103 this->name->Release();
104 this->info->Release();
105 this->url->Release();
106
107 for (uint i = 0; i < this->param_info.Length(); i++) delete this->param_info[i];
108 }
109
114 void GRFConfig::CopyParams(const GRFConfig &src)
115 {
116 this->num_params = src.num_params;
117 this->num_valid_params = src.num_valid_params;
118 MemCpyT<uint32>(this->param, src.param, lengthof(this->param));
119 }
120
126 const char *GRFConfig::GetName() const
127 {
128 const char *name = GetGRFStringFromGRFText(this->name->text);
129 return StrEmpty(name) ? this->filename : name;
130 }
131
136 const char *GRFConfig::GetDescription() const
137 {
138 return GetGRFStringFromGRFText(this->info->text);
139 }
140
145 const char *GRFConfig::GetURL() const
146 {
147 return GetGRFStringFromGRFText(this->url->text);
148 }
149
151 void GRFConfig::SetParameterDefaults()
152 {
153 this->num_params = 0;
154 MemSetT<uint32>(this->param, 0, lengthof(this->param));
155
156 if (!this->has_param_defaults) return;
157
158 for (uint i = 0; i < this->param_info.Length(); i++) {
159 if (this->param_info[i] == NULL) continue;
160 this->param_info[i]->SetValue(this, this->param_info[i]->def_value);
161 }
162 }
163
169 void GRFConfig::SetSuitablePalette()
170 {
171 PaletteType pal;
172 switch (this->palette & GRFP_GRF_MASK) {
173 case GRFP_GRF_DOS: pal = PAL_DOS; break;
174 case GRFP_GRF_WINDOWS: pal = PAL_WINDOWS; break;
175 default: pal = _settings_client.gui.newgrf_default_palette == 1 ? PAL_WINDOWS : PAL_DOS; break;
176 }
177 SB(this->palette, GRFP_USE_BIT, 1, pal == PAL_WINDOWS ? GRFP_USE_WINDOWS : GRFP_USE_DOS);
178 }
179
183 void GRFConfig::FinalizeParameterInfo()
184 {
185 for (GRFParameterInfo **info = this->param_info.Begin(); info != this->param_info.End(); ++info) {
186 if (*info == NULL) continue;
187 (*info)->Finalize();
188 }
189 }
190
191 GRFConfig *_all_grfs;
192 GRFConfig *_grfconfig;
193 GRFConfig *_grfconfig_newgame;
194 GRFConfig *_grfconfig_static;
195 uint _missing_extra_graphics = 0;
196
202 GRFError::GRFError(StringID severity, StringID message) :
203 message(message),
204 severity(severity)
205 {
206 }
207
212 GRFError::GRFError(const GRFError &error) :
213 ZeroedMemoryAllocator(),
214 custom_message(error.custom_message),
215 data(error.data),
216 message(error.message),
217 severity(error.severity)
218 {
219 if (error.custom_message != NULL) this->custom_message = stredup(error.custom_message);
220 if (error.data != NULL) this->data = stredup(error.data);
221 memcpy(this->param_value, error.param_value, sizeof(this->param_value));
222 }
223
224 GRFError::~GRFError()
225 {
226 free(this->custom_message);
227 free(this->data);
228 }
229
234 GRFParameterInfo::GRFParameterInfo(uint nr) :
235 name(NULL),
236 desc(NULL),
237 type(PTYPE_UINT_ENUM),
238 min_value(0),
239 max_value(UINT32_MAX),
240 def_value(0),
241 param_nr(nr),
242 first_bit(0),
243 num_bit(32),
244 complete_labels(false)
245 {}
246
252 GRFParameterInfo::GRFParameterInfo(GRFParameterInfo &info) :
253 name(DuplicateGRFText(info.name)),
254 desc(DuplicateGRFText(info.desc)),
255 type(info.type),
256 min_value(info.min_value),
257 max_value(info.max_value),
258 def_value(info.def_value),
259 param_nr(info.param_nr),
260 first_bit(info.first_bit),
261 num_bit(info.num_bit),
262 complete_labels(info.complete_labels)
263 {
264 for (uint i = 0; i < info.value_names.Length(); i++) {
265 SmallPair<uint32, GRFText *> *data = info.value_names.Get(i);
266 this->value_names.Insert(data->first, DuplicateGRFText(data->second));
267 }
268 }
269
271 GRFParameterInfo::~GRFParameterInfo()
272 {
273 CleanUpGRFText(this->name);
274 CleanUpGRFText(this->desc);
275 for (uint i = 0; i < this->value_names.Length(); i++) {
276 SmallPair<uint32, GRFText *> *data = this->value_names.Get(i);
277 CleanUpGRFText(data->second);
278 }
279 }
280
286 uint32 GRFParameterInfo::GetValue(struct GRFConfig *config) const
287 {
288 /* GB doesn't work correctly with nbits == 32, so handle that case here. */
289 if (this->num_bit == 32) return config->param[this->param_nr];
290 return GB(config->param[this->param_nr], this->first_bit, this->num_bit);
291 }
292
298 void GRFParameterInfo::SetValue(struct GRFConfig *config, uint32 value)
299 {
300 /* SB doesn't work correctly with nbits == 32, so handle that case here. */
301 if (this->num_bit == 32) {
302 config->param[this->param_nr] = value;
303 } else {
304 SB(config->param[this->param_nr], this->first_bit, this->num_bit, value);
305 }
306 config->num_params = max<uint>(config->num_params, this->param_nr + 1);
307 SetWindowDirty(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE);
308 }
309
313 void GRFParameterInfo::Finalize()
314 {
315 this->complete_labels = true;
316 for (uint32 value = this->min_value; value <= this->max_value; value++) {
317 if (!this->value_names.Contains(value)) {
318 this->complete_labels = false;
319 break;
320 }
321 }
322 }
323
330 bool UpdateNewGRFConfigPalette(int32 p1)
331 {
332 for (GRFConfig *c = _grfconfig_newgame; c != NULL; c = c->next) c->SetSuitablePalette();
333 for (GRFConfig *c = _grfconfig_static; c != NULL; c = c->next) c->SetSuitablePalette();
334 for (GRFConfig *c = _all_grfs; c != NULL; c = c->next) c->SetSuitablePalette();
335 return true;
336 }
337
343 size_t GRFGetSizeOfDataSection(FILE *f)
344 {
345 extern const byte _grf_cont_v2_sig[];
346 static const uint header_len = 14;
347
348 byte data[header_len];
349 if (fread(data, 1, header_len, f) == header_len) {
350 if (data[0] == 0 && data[1] == 0 && MemCmpT(data + 2, _grf_cont_v2_sig, 8) == 0) {
351 /* Valid container version 2, get data section size. */
352 size_t offset = ((size_t)data[13] << 24) | ((size_t)data[12] << 16) | ((size_t)data[11] << 8) | (size_t)data[10];
353 if (offset >= 1 * 1024 * 1024 * 1024) {
354 DEBUG(grf, 0, "Unexpectedly large offset for NewGRF");
355 /* Having more than 1 GiB of data is very implausible. Mostly because then
356 * all pools in OpenTTD are flooded already. Or it's just Action C all over.
357 * In any case, the offsets to graphics will likely not work either. */
358 return SIZE_MAX;
359 }
360 return header_len + offset;
361 }
362 }
363
364 return SIZE_MAX;
365 }
366
373 static bool CalcGRFMD5Sum(GRFConfig *config, Subdirectory subdir)
374 {
375 FILE *f;
376 Md5 checksum;
377 uint8 buffer[1024];
378 size_t len, size;
379
380 /* open the file */
381 f = FioFOpenFile(config->filename, "rb", subdir, &size);
382 if (f == NULL) return false;
383
384 long start = ftell(f);
385 size = min(size, GRFGetSizeOfDataSection(f));
386
387 if (start < 0 || fseek(f, start, SEEK_SET) < 0) {
388 FioFCloseFile(f);
389 return false;
390 }
391
392 /* calculate md5sum */
393 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
394 size -= len;
395 checksum.Append(buffer, len);
396 }
397 checksum.Finish(config->ident.md5sum);
398
399 FioFCloseFile(f);
400
401 return true;
402 }
403
404
412 bool FillGRFDetails(GRFConfig *config, bool is_static, Subdirectory subdir)
413 {
414 if (!FioCheckFileExists(config->filename, subdir)) {
415 config->status = GCS_NOT_FOUND;
416 return false;
417 }
418
419 /* Find and load the Action 8 information */
420 LoadNewGRFFile(config, CONFIG_SLOT, GLS_FILESCAN, subdir);
421 config->SetSuitablePalette();
422 config->FinalizeParameterInfo();
423
424 /* Skip if the grfid is 0 (not read) or if it is an internal GRF */
425 if (config->ident.grfid == 0 || HasBit(config->flags, GCF_SYSTEM)) return false;
426
427 if (is_static) {
428 /* Perform a 'safety scan' for static GRFs */
429 LoadNewGRFFile(config, CONFIG_SLOT, GLS_SAFETYSCAN, subdir);
430
431 /* GCF_UNSAFE is set if GLS_SAFETYSCAN finds unsafe actions */
432 if (HasBit(config->flags, GCF_UNSAFE)) return false;
433 }
434
435 return CalcGRFMD5Sum(config, subdir);
436 }
437
438
444 void ClearGRFConfigList(GRFConfig **config)
445 {
446 GRFConfig *c, *next;
447 for (c = *config; c != NULL; c = next) {
448 next = c->next;
449 delete c;
450 }
451 *config = NULL;
452 }
453
454
462 GRFConfig **CopyGRFConfigList(GRFConfig **dst, const GRFConfig *src, bool init_only)
463 {
464 /* Clear destination as it will be overwritten */
465 ClearGRFConfigList(dst);
466 for (; src != NULL; src = src->next) {
467 GRFConfig *c = new GRFConfig(*src);
468
469 ClrBit(c->flags, GCF_INIT_ONLY);
470 if (init_only) SetBit(c->flags, GCF_INIT_ONLY);
471
472 *dst = c;
473 dst = &c->next;
474 }
475
476 return dst;
477 }
478
492 static void RemoveDuplicatesFromGRFConfigList(GRFConfig *list)
493 {
494 GRFConfig *prev;
495 GRFConfig *cur;
496
497 if (list == NULL) return;
498
499 for (prev = list, cur = list->next; cur != NULL; prev = cur, cur = cur->next) {
500 if (cur->ident.grfid != list->ident.grfid) continue;
501
502 prev->next = cur->next;
503 delete cur;
504 cur = prev; // Just go back one so it continues as normal later on
505 }
506
507 RemoveDuplicatesFromGRFConfigList(list->next);
508 }
509
514 void AppendStaticGRFConfigs(GRFConfig **dst)
515 {
516 GRFConfig **tail = dst;
517 while (*tail != NULL) tail = &(*tail)->next;
518
519 CopyGRFConfigList(tail, _grfconfig_static, false);
520 RemoveDuplicatesFromGRFConfigList(*dst);
521 }
522
528 void AppendToGRFConfigList(GRFConfig **dst, GRFConfig *el)
529 {
530 GRFConfig **tail = dst;
531 while (*tail != NULL) tail = &(*tail)->next;
532 *tail = el;
533
534 RemoveDuplicatesFromGRFConfigList(*dst);
535 }
536
537
539 void ResetGRFConfig(bool defaults)
540 {
541 CopyGRFConfigList(&_grfconfig, _grfconfig_newgame, !defaults);
542 AppendStaticGRFConfigs(&_grfconfig);
543 }
544
545
557 GRFListCompatibility IsGoodGRFConfigList(GRFConfig *grfconfig)
558 {
559 GRFListCompatibility res = GLC_ALL_GOOD;
560
561 for (GRFConfig *c = grfconfig; c != NULL; c = c->next) {
562 const GRFConfig *f = FindGRFConfig(c->ident.grfid, FGCM_EXACT, c->ident.md5sum);
563 if (f == NULL || HasBit(f->flags, GCF_INVALID)) {
564 char buf[256];
565
566 /* If we have not found the exactly matching GRF try to find one with the
567 * same grfid, as it most likely is compatible */
568 f = FindGRFConfig(c->ident.grfid, FGCM_COMPATIBLE, NULL, c->version);
569 if (f != NULL) {
570 md5sumToString(buf, lastof(buf), c->ident.md5sum);
571 DEBUG(grf, 1, "NewGRF %08X (%s) not found; checksum %s. Compatibility mode on", BSWAP32(c->ident.grfid), c->filename, buf);
572 if (!HasBit(c->flags, GCF_COMPATIBLE)) {
573 /* Preserve original_md5sum after it has been assigned */
574 SetBit(c->flags, GCF_COMPATIBLE);
575 memcpy(c->original_md5sum, c->ident.md5sum, sizeof(c->original_md5sum));
576 }
577
578 /* Non-found has precedence over compatibility load */
579 if (res != GLC_NOT_FOUND) res = GLC_COMPATIBLE;
580 goto compatible_grf;
581 }
582
583 /* No compatible grf was found, mark it as disabled */
584 md5sumToString(buf, lastof(buf), c->ident.md5sum);
585 DEBUG(grf, 0, "NewGRF %08X (%s) not found; checksum %s", BSWAP32(c->ident.grfid), c->filename, buf);
586
587 c->status = GCS_NOT_FOUND;
588 res = GLC_NOT_FOUND;
589 } else {
590 compatible_grf:
591 DEBUG(grf, 1, "Loading GRF %08X from %s", BSWAP32(f->ident.grfid), f->filename);
592 /* The filename could be the filename as in the savegame. As we need
593 * to load the GRF here, we need the correct filename, so overwrite that
594 * in any case and set the name and info when it is not set already.
595 * When the GCF_COPY flag is set, it is certain that the filename is
596 * already a local one, so there is no need to replace it. */
597 if (!HasBit(c->flags, GCF_COPY)) {
598 free(c->filename);
599 c->filename = stredup(f->filename);
600 memcpy(c->ident.md5sum, f->ident.md5sum, sizeof(c->ident.md5sum));
601 c->name->Release();
602 c->name = f->name;
603 c->name->AddRef();
604 c->info->Release();
605 c->info = f->name;
606 c->info->AddRef();
607 c->error = NULL;
608 c->version = f->version;
609 c->min_loadable_version = f->min_loadable_version;
610 c->num_valid_params = f->num_valid_params;
611 c->has_param_defaults = f->has_param_defaults;
612 for (uint i = 0; i < f->param_info.Length(); i++) {
613 if (f->param_info[i] == NULL) {
614 *c->param_info.Append() = NULL;
615 } else {
616 *c->param_info.Append() = new GRFParameterInfo(*f->param_info[i]);
617 }
618 }
619 }
620 }
621 }
622
623 return res;
624 }
625
627 class GRFFileScanner : FileScanner {
628 uint next_update;
629 uint num_scanned;
630
631 public:
632 GRFFileScanner() : next_update(_realtime_tick), num_scanned(0)
633 {
634 }
635
636 /* virtual */ bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename);
637
639 static uint DoScan()
640 {
641 GRFFileScanner fs;
642 int ret = fs.Scan(".grf", NEWGRF_DIR);
643 /* The number scanned and the number returned may not be the same;
644 * duplicate NewGRFs and base sets are ignored in the return value. */
645 _settings_client.gui.last_newgrf_count = fs.num_scanned;
646 return ret;
647 }
648 };
649
650 bool GRFFileScanner::AddFile(const char *filename, size_t basepath_length, const char *tar_filename)
651 {
652 GRFConfig *c = new GRFConfig(filename + basepath_length);
653
654 bool added = true;
655 if (FillGRFDetails(c, false)) {
656 if (_all_grfs == NULL) {
657 _all_grfs = c;
658 } else {
659 /* Insert file into list at a position determined by its
660 * name, so the list is sorted as we go along */
661 GRFConfig **pd, *d;
662 bool stop = false;
663 for (pd = &_all_grfs; (d = *pd) != NULL; pd = &d->next) {
664 if (c->ident.grfid == d->ident.grfid && memcmp(c->ident.md5sum, d->ident.md5sum, sizeof(c->ident.md5sum)) == 0) added = false;
665 /* Because there can be multiple grfs with the same name, make sure we checked all grfs with the same name,
666 * before inserting the entry. So insert a new grf at the end of all grfs with the same name, instead of
667 * just after the first with the same name. Avoids doubles in the list. */
668 if (strcasecmp(c->GetName(), d->GetName()) <= 0) {
669 stop = true;
670 } else if (stop) {
671 break;
672 }
673 }
674 if (added) {
675 c->next = d;
676 *pd = c;
677 }
678 }
679 } else {
680 added = false;
681 }
682
683 this->num_scanned++;
684 if (this->next_update <= _realtime_tick) {
685 _modal_progress_work_mutex->EndCritical();
686 _modal_progress_paint_mutex->BeginCritical();
687
688 const char *name = NULL;
689 if (c->name != NULL) name = GetGRFStringFromGRFText(c->name->text);
690 if (name == NULL) name = c->filename;
691 UpdateNewGRFScanStatus(this->num_scanned, name);
692
693 _modal_progress_work_mutex->BeginCritical();
694 _modal_progress_paint_mutex->EndCritical();
695
696 this->next_update = _realtime_tick + 200;
697 }
698
699 if (!added) {
700 /* File couldn't be opened, or is either not a NewGRF or is a
701 * 'system' NewGRF or it's already known, so forget about it. */
702 delete c;
703 }
704
705 return added;
706 }
707
714 static int CDECL GRFSorter(GRFConfig * const *p1, GRFConfig * const *p2)
715 {
716 const GRFConfig *c1 = *p1;
717 const GRFConfig *c2 = *p2;
718
719 return strnatcmp(c1->GetName(), c2->GetName());
720 }
721
726 void DoScanNewGRFFiles(void *callback)
727 {
728 _modal_progress_work_mutex->BeginCritical();
729
730 ClearGRFConfigList(&_all_grfs);
731 TarScanner::DoScan(TarScanner::NEWGRF);
732
733 DEBUG(grf, 1, "Scanning for NewGRFs");
734 uint num = GRFFileScanner::DoScan();
735
736 DEBUG(grf, 1, "Scan complete, found %d files", num);
737 if (num != 0 && _all_grfs != NULL) {
738 /* Sort the linked list using quicksort.
739 * For that we first have to make an array, then sort and
740 * then remake the linked list. */
741 GRFConfig **to_sort = MallocT<GRFConfig*>(num);
742
743 uint i = 0;
744 for (GRFConfig *p = _all_grfs; p != NULL; p = p->next, i++) {
745 to_sort[i] = p;
746 }
747 /* Number of files is not necessarily right */
748 num = i;
749
750 QSortT(to_sort, num, &GRFSorter);
751
752 for (i = 1; i < num; i++) {
753 to_sort[i - 1]->next = to_sort[i];
754 }
755 to_sort[num - 1]->next = NULL;
756 _all_grfs = to_sort[0];
757
758 free(to_sort);
759
760 #ifdef ENABLE_NETWORK
761 NetworkAfterNewGRFScan();
762 #endif
763 }
764
765 _modal_progress_work_mutex->EndCritical();
766 _modal_progress_paint_mutex->BeginCritical();
767
768 /* Yes... these are the NewGRF windows */
769 InvalidateWindowClassesData(WC_SAVELOAD, 0, true);
770 InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_NEWGRF_STATE, GOID_NEWGRF_RESCANNED, true);
771 if (callback != NULL) ((NewGRFScanCallback*)callback)->OnNewGRFsScanned();
772
773 DeleteWindowByClass(WC_MODAL_PROGRESS);
774 SetModalProgress(false);
775 MarkWholeScreenDirty();
776 _modal_progress_paint_mutex->EndCritical();
777 }
778
783 void ScanNewGRFFiles(NewGRFScanCallback *callback)
784 {
785 /* First set the modal progress. This ensures that it will eventually let go of the paint mutex. */
786 SetModalProgress(true);
787 /* Only then can we really start, especially by marking the whole screen dirty. Get those other windows hidden!. */
788 MarkWholeScreenDirty();
789
790 if (!VideoDriver::GetInstance()->HasGUI() || !ThreadObject::New(&DoScanNewGRFFiles, callback, NULL, "ottd:newgrf-scan")) {
791 _modal_progress_work_mutex->EndCritical();
792 _modal_progress_paint_mutex->EndCritical();
793 DoScanNewGRFFiles(callback);
794 _modal_progress_paint_mutex->BeginCritical();
795 _modal_progress_work_mutex->BeginCritical();
796 } else {
797 UpdateNewGRFScanStatus(0, NULL);
798 }
799 }
800
809 const GRFConfig *FindGRFConfig(uint32 grfid, FindGRFConfigMode mode, const uint8 *md5sum, uint32 desired_version)
810 {
811 assert((mode == FGCM_EXACT) != (md5sum == NULL));
812 const GRFConfig *best = NULL;
813 for (const GRFConfig *c = _all_grfs; c != NULL; c = c->next) {
814 /* if md5sum is set, we look for an exact match and continue if not found */
815 if (!c->ident.HasGrfIdentifier(grfid, md5sum)) continue;
816 /* return it, if the exact same newgrf is found, or if we do not care about finding "the best" */
817 if (md5sum != NULL || mode == FGCM_ANY) return c;
818 /* Skip incompatible stuff, unless explicitly allowed */
819 if (mode != FGCM_NEWEST && HasBit(c->flags, GCF_INVALID)) continue;
820 /* check version compatibility */
821 if (mode == FGCM_COMPATIBLE && (c->version < desired_version || c->min_loadable_version > desired_version)) continue;
822 /* remember the newest one as "the best" */
823 if (best == NULL || c->version > best->version) best = c;
824 }
825
826 return best;
827 }
828
829 #ifdef ENABLE_NETWORK
830
832 struct UnknownGRF : public GRFIdentifier {
833 UnknownGRF *next;
834 GRFTextWrapper *name;
835 };
836
854 GRFTextWrapper *FindUnknownGRFName(uint32 grfid, uint8 *md5sum, bool create)
855 {
856 UnknownGRF *grf;
857 static UnknownGRF *unknown_grfs = NULL;
858
859 for (grf = unknown_grfs; grf != NULL; grf = grf->next) {
860 if (grf->grfid == grfid) {
861 if (memcmp(md5sum, grf->md5sum, sizeof(grf->md5sum)) == 0) return grf->name;
862 }
863 }
864
865 if (!create) return NULL;
866
867 grf = CallocT<UnknownGRF>(1);
868 grf->grfid = grfid;
869 grf->next = unknown_grfs;
870 grf->name = new GRFTextWrapper();
871 grf->name->AddRef();
872
873 AddGRFTextToList(&grf->name->text, UNKNOWN_GRF_NAME_PLACEHOLDER);
874 memcpy(grf->md5sum, md5sum, sizeof(grf->md5sum));
875
876 unknown_grfs = grf;
877 return grf->name;
878 }
879
880 #endif /* ENABLE_NETWORK */
881
882
889 GRFConfig *GetGRFConfig(uint32 grfid, uint32 mask)
890 {
891 GRFConfig *c;
892
893 for (c = _grfconfig; c != NULL; c = c->next) {
894 if ((c->ident.grfid & mask) == (grfid & mask)) return c;
895 }
896
897 return NULL;
898 }
899
900
902 char *GRFBuildParamList(char *dst, const GRFConfig *c, const char *last)
903 {
904 uint i;
905
906 /* Return an empty string if there are no parameters */
907 if (c->num_params == 0) return strecpy(dst, "", last);
908
909 for (i = 0; i < c->num_params; i++) {
910 if (i > 0) dst = strecpy(dst, " ", last);
911 dst += seprintf(dst, last, "%d", c->param[i]);
912 }
913 return dst;
914 }
915
917 static const uint32 OPENTTD_GRAPHICS_BASE_GRF_ID = BSWAP32(0xFF4F5400);
918
924 const char *GRFConfig::GetTextfile(TextfileType type) const
925 {
926 return ::GetTextfile(type, NEWGRF_DIR, this->filename);
927 }