Make it possible to stop road vehicles from slowing down in curves so "diagonal"...
[openttd-joker.git] / src / gfxinit.cpp
blob99f37faaff7a4f0961231ba09698e8cf0a3a490b
1 /* $Id$ */
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 */
10 /** @file gfxinit.cpp Initializing of the (GRF) graphics. */
12 #include "stdafx.h"
13 #include "fios.h"
14 #include "newgrf.h"
15 #include "3rdparty/md5/md5.h"
16 #include "fontcache.h"
17 #include "gfx_func.h"
18 #include "transparency.h"
19 #include "blitter/factory.hpp"
20 #include "video/video_driver.hpp"
21 #include "window_func.h"
22 #include "zoom_func.h"
23 #include "clear_map.h"
24 #include "clear_func.h"
25 #include "tree_map.h"
26 #include "table/tree_land.h"
27 #include "blitter/32bpp_base.hpp"
29 /* The type of set we're replacing */
30 #define SET_TYPE "graphics"
31 #include "base_media_func.h"
33 #include "table/sprites.h"
35 #include "safeguards.h"
37 /** Whether the given NewGRFs must get a palette remap from windows to DOS or not. */
38 bool _palette_remap_grf[MAX_FILE_SLOTS];
40 #include "table/landscape_sprite.h"
42 /** Offsets for loading the different "replacement" sprites in the files. */
43 static const SpriteID * const _landscape_spriteindexes[] = {
44 _landscape_spriteindexes_arctic,
45 _landscape_spriteindexes_tropic,
46 _landscape_spriteindexes_toyland,
49 /**
50 * Load an old fashioned GRF file.
51 * @param filename The name of the file to open.
52 * @param load_index The offset of the first sprite.
53 * @param file_index The Fio offset to load the file in.
54 * @return The number of loaded sprites.
56 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
58 uint load_index_org = load_index;
59 uint sprite_id = 0;
61 FioOpenFile(file_index, filename, BASESET_DIR);
63 DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
65 byte container_ver = GetGRFContainerVersion();
66 if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
67 ReadGRFSpriteOffsets(container_ver);
68 if (container_ver >= 2) {
69 /* Read compression. */
70 byte compression = FioReadByte();
71 if (compression != 0) usererror("Unsupported compression format");
74 while (LoadNextSprite(load_index, file_index, sprite_id, container_ver)) {
75 load_index++;
76 sprite_id++;
77 if (load_index >= MAX_SPRITES) {
78 usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
81 DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
83 return load_index - load_index_org;
86 /**
87 * Load an old fashioned GRF file to replace already loaded sprites.
88 * @param filename The name of the file to open.
89 * @param index_tlb The offsets of each of the sprites.
90 * @param file_index The Fio offset to load the file in.
91 * @return The number of loaded sprites.
93 static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
95 uint start;
96 uint sprite_id = 0;
98 FioOpenFile(file_index, filename, BASESET_DIR);
100 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
102 byte container_ver = GetGRFContainerVersion();
103 if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
104 ReadGRFSpriteOffsets(container_ver);
105 if (container_ver >= 2) {
106 /* Read compression. */
107 byte compression = FioReadByte();
108 if (compression != 0) usererror("Unsupported compression format");
111 while ((start = *index_tbl++) != END) {
112 uint end = *index_tbl++;
114 do {
115 bool b = LoadNextSprite(start, file_index, sprite_id, container_ver);
116 assert(b);
117 sprite_id++;
118 } while (++start <= end);
123 * Checks whether the MD5 checksums of the files are correct.
125 * @note Also checks sample.cat and other required non-NewGRF GRFs for corruption.
127 void CheckExternalFiles()
129 if (BaseGraphics::GetUsedSet() == nullptr || BaseSounds::GetUsedSet() == nullptr) return;
131 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
133 DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
135 static const size_t ERROR_MESSAGE_LENGTH = 256;
136 static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
138 /* Allocate for a message for each missing file and for one error
139 * message per set.
141 char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
142 error_msg[0] = '\0';
143 char *add_pos = error_msg;
144 const char *last = lastof(error_msg);
146 if (used_set->GetNumInvalid() != 0) {
147 /* Not all files were loaded successfully, see which ones */
148 add_pos += seprintf(add_pos, last, "Trying to load graphics set '%s', but it is incomplete. The game will probably not run correctly until you properly install this set or select another one. See section 4.1 of readme.txt.\n\nThe following files are corrupted or missing:\n", used_set->name);
149 for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
150 MD5File::ChecksumResult res = GraphicsSet::CheckMD5(&used_set->files[i], BASESET_DIR);
151 if (res != MD5File::CR_MATCH) add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", used_set->files[i].filename, res == MD5File::CR_MISMATCH ? "corrupt" : "missing", used_set->files[i].missing_warning);
153 add_pos += seprintf(add_pos, last, "\n");
156 const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
157 if (sounds_set->GetNumInvalid() != 0) {
158 add_pos += seprintf(add_pos, last, "Trying to load sound set '%s', but it is incomplete. The game will probably not run correctly until you properly install this set or select another one. See section 4.1 of readme.txt.\n\nThe following files are corrupted or missing:\n", sounds_set->name);
160 assert_compile(SoundsSet::NUM_FILES == 1);
161 /* No need to loop each file, as long as there is only a single
162 * sound file. */
163 add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", sounds_set->files->filename, SoundsSet::CheckMD5(sounds_set->files, BASESET_DIR) == MD5File::CR_MISMATCH ? "corrupt" : "missing", sounds_set->files->missing_warning);
166 if (add_pos != error_msg) ShowInfoF("%s", error_msg);
169 /** Actually load the sprite tables. */
170 static void LoadSpriteTables()
172 memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
173 uint i = FIRST_GRF_SLOT;
174 const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
176 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
177 LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
179 /* Tracerestrict sprites. */
180 LoadGrfFile("tracerestrict.grf", SPR_TRACERESTRICT_BASE, i++);
183 * The second basic file always starts at the given location and does
184 * contain a different amount of sprites depending on the "type"; DOS
185 * has a few sprites less. However, we do not care about those missing
186 * sprites as they are not shown anyway (logos in intro game).
188 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
189 LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
192 * Load additional sprites for climates other than temperate.
193 * This overwrites some of the temperate sprites, such as foundations
194 * and the ground sprites.
196 if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
197 _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
198 LoadGrfFileIndexed(
199 used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
200 _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
205 /* Load route step graphics */
206 LoadGrfFile("route_step.grf", SPR_ROUTE_STEP_BASE, i++);
208 /* Load zoning */
209 LoadGrfFile("innerhighlight.grf", SPR_ZONING_INNER_HIGHLIGHT_BASE, i++);
211 /* Initialize the unicode to sprite mapping table */
212 InitializeUnicodeGlyphMap();
214 /* Overgrown tracks */
215 LoadGrfFile("oldtracks.grf", SPR_OLDTRACKS_BASE, i++);
218 * Load the base and extra NewGRF with OTTD required graphics as first NewGRF.
219 * However, we do not want it to show up in the list of used NewGRFs,
220 * so we have to manually add it, and then remove it later.
222 GRFConfig *top = _grfconfig;
224 /* Default extra graphics */
225 GRFConfig *master = new GRFConfig("OPENTTD.GRF");
226 master->palette |= GRFP_GRF_DOS;
227 FillGRFDetails(master, false, BASESET_DIR);
228 ClrBit(master->flags, GCF_INIT_ONLY);
230 /* Baseset extra graphics */
231 GRFConfig *extra = new GRFConfig(used_set->files[GFT_EXTRA].filename);
233 /* We know the palette of the base set, so if the base NewGRF is not
234 * setting one, use the palette of the base set and not the global
235 * one which might be the wrong palette for this base NewGRF.
236 * The value set here might be overridden via action14 later. */
237 switch (used_set->palette) {
238 case PAL_DOS: extra->palette |= GRFP_GRF_DOS; break;
239 case PAL_WINDOWS: extra->palette |= GRFP_GRF_WINDOWS; break;
240 default: break;
242 FillGRFDetails(extra, false, BASESET_DIR);
243 ClrBit(extra->flags, GCF_INIT_ONLY);
245 extra->next = top;
246 master->next = extra;
247 _grfconfig = master;
249 LoadNewGRF(SPR_NEWGRFS_BASE, i, 2);
251 uint total_extra_graphics = SPR_NEWGRFS_BASE - SPR_OPENTTD_BASE;
252 _missing_extra_graphics = GetSpriteCountForSlot(i, SPR_OPENTTD_BASE, SPR_NEWGRFS_BASE);
253 DEBUG(sprite, 1, "%u extra sprites, %u from baseset, %u from fallback", total_extra_graphics, total_extra_graphics - _missing_extra_graphics, _missing_extra_graphics);
255 /* The original baseset extra graphics intentionally make use of the fallback graphics.
256 * Let's say everything which provides less than 500 sprites misses the rest intentionally. */
257 if (500 + _missing_extra_graphics > total_extra_graphics) _missing_extra_graphics = 0;
259 /* Free and remove the top element. */
260 delete extra;
261 delete master;
262 _grfconfig = top;
267 * Check blitter needed by NewGRF config and switch if needed.
268 * @return False when nothing changed, true otherwise.
270 static bool SwitchNewGRFBlitter()
272 /* Never switch if the blitter was specified by the user. */
273 if (!_blitter_autodetected) return false;
275 /* Null driver => dedicated server => do nothing. */
276 if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return false;
278 /* Get preferred depth.
279 * - depth_wanted_by_base: Depth required by the baseset, i.e. the majority of the sprites.
280 * - depth_wanted_by_grf: Depth required by some NewGRF.
281 * Both can force using a 32bpp blitter. depth_wanted_by_base is used to select
282 * between multiple 32bpp blitters, which perform differently with 8bpp sprites.
284 uint depth_wanted_by_base = BaseGraphics::GetUsedSet()->blitter == BLT_32BPP ? 32 : 8;
285 uint depth_wanted_by_grf = _support8bpp == S8BPP_NONE ? 32 : 8;
286 for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
287 if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND || HasBit(c->flags, GCF_INIT_ONLY)) continue;
288 if (c->palette & GRFP_BLT_32BPP) depth_wanted_by_grf = 32;
291 /* Search the best blitter. */
292 static const struct {
293 const char *name;
294 uint animation; ///< 0: no support, 1: do support, 2: both
295 uint min_base_depth, max_base_depth, min_grf_depth, max_grf_depth;
296 } replacement_blitters[] = {
297 #ifdef WITH_SSE
298 { "32bpp-sse4", 0, 32, 32, 8, 32 },
299 { "32bpp-ssse3", 0, 32, 32, 8, 32 },
300 { "32bpp-sse2", 0, 32, 32, 8, 32 },
301 { "32bpp-sse4-anim", 1, 32, 32, 8, 32 },
302 #endif
303 { "8bpp-optimized", 2, 8, 8, 8, 8 },
304 { "32bpp-optimized", 0, 8, 32, 8, 32 },
305 #ifdef WITH_SSE
306 { "32bpp-sse2-anim", 1, 8, 32, 8, 32 },
307 #endif
308 { "32bpp-anim", 1, 8, 32, 8, 32 },
311 const bool animation_wanted = HasBit(_display_opt, DO_FULL_ANIMATION);
312 const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName();
314 VideoDriver::GetInstance()->AcquireBlitterLock();
316 for (uint i = 0; i < lengthof(replacement_blitters); i++) {
317 if (animation_wanted && (replacement_blitters[i].animation == 0)) continue;
318 if (!animation_wanted && (replacement_blitters[i].animation == 1)) continue;
320 if (!IsInsideMM(depth_wanted_by_base, replacement_blitters[i].min_base_depth, replacement_blitters[i].max_base_depth + 1)) continue;
321 if (!IsInsideMM(depth_wanted_by_grf, replacement_blitters[i].min_grf_depth, replacement_blitters[i].max_grf_depth + 1)) continue;
322 const char *repl_blitter = replacement_blitters[i].name;
324 if (strcmp(repl_blitter, cur_blitter) == 0) {
325 VideoDriver::GetInstance()->ReleaseBlitterLock();
326 return false;
328 if (BlitterFactory::GetBlitterFactory(repl_blitter) == nullptr) continue;
330 DEBUG(misc, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter);
331 Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter);
332 if (new_blitter == nullptr) NOT_REACHED();
333 DEBUG(misc, 1, "Successfully switched to %s.", repl_blitter);
334 break;
337 if (!VideoDriver::GetInstance()->AfterBlitterChange()) {
338 /* Failed to switch blitter, let's hope we can return to the old one. */
339 if (BlitterFactory::SelectBlitter(cur_blitter) == nullptr || !VideoDriver::GetInstance()->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config");
342 VideoDriver::GetInstance()->ReleaseBlitterLock();
344 return true;
347 /** Check whether we still use the right blitter, or use another (better) one. */
348 void CheckBlitter()
350 if (!SwitchNewGRFBlitter()) return;
352 ClearFontCache();
353 GfxClearSpriteCache();
354 ReInitAllWindows();
357 static void UpdateRouteStepSpriteSize()
359 extern uint _vp_route_step_width;
360 extern uint _vp_route_step_height_top;
361 extern uint _vp_route_step_height_middle;
362 extern uint _vp_route_step_height_bottom;
363 extern SubSprite _vp_route_step_subsprite;
365 Dimension d = GetSpriteSize(SPR_ROUTE_STEP_TOP);
366 _vp_route_step_width = d.width;
367 _vp_route_step_height_top = d.height;
369 d = GetSpriteSize(SPR_ROUTE_STEP_MIDDLE);
370 _vp_route_step_height_middle = d.height;
371 assert(_vp_route_step_width == d.width);
373 d = GetSpriteSize(SPR_ROUTE_STEP_BOTTOM);
374 _vp_route_step_height_bottom = d.height;
375 assert(_vp_route_step_width == d.width);
377 const int char_height = GetCharacterHeight(FS_SMALL) + 1;
378 _vp_route_step_subsprite.right = ScaleByZoom(_vp_route_step_width, ZOOM_LVL_GUI);
379 _vp_route_step_subsprite.bottom = ScaleByZoom(char_height, ZOOM_LVL_GUI);
380 _vp_route_step_subsprite.left = 0;
381 _vp_route_step_subsprite.top = 0;
384 /* multi can be density, field type, ... */
385 static SpriteID GetSpriteIDForClearGround(const ClearGround cg, const Slope slope, const uint multi)
387 switch (cg) {
388 case CLEAR_GRASS:
389 return GetSpriteIDForClearLand(slope, (byte)multi);
390 case CLEAR_ROUGH:
391 return GetSpriteIDForHillyLand(slope, multi);
392 case CLEAR_ROCKS:
393 return GetSpriteIDForRocks(slope, multi);
394 case CLEAR_FIELDS:
395 return GetSpriteIDForFields(slope, multi);
396 case CLEAR_SNOW:
397 case CLEAR_DESERT:
398 return GetSpriteIDForSnowDesert(slope, multi);
399 default: NOT_REACHED();
403 /** Once the sprites are loaded, we can determine main colours of ground/water/... */
404 void GfxDetermineMainColours()
406 /* Water. */
407 extern uint32 _vp_map_water_colour[5];
408 _vp_map_water_colour[0] = GetSpriteMainColour(SPR_FLAT_WATER_TILE, PAL_NONE);
409 if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 32) {
410 _vp_map_water_colour[1] = Blitter_32bppBase::MakeTransparent(_vp_map_water_colour[0], 256, 192).data; // lighter
411 _vp_map_water_colour[2] = Blitter_32bppBase::MakeTransparent(_vp_map_water_colour[0], 192, 256).data; // darker
412 _vp_map_water_colour[3] = _vp_map_water_colour[2];
413 _vp_map_water_colour[4] = _vp_map_water_colour[1];
416 /* Clear ground. */
417 extern uint32 _vp_map_vegetation_clear_colours[16][6][8];
418 memset(_vp_map_vegetation_clear_colours, 0, sizeof(_vp_map_vegetation_clear_colours));
419 const struct {
420 byte min;
421 byte max;
422 } multi[6] = {
423 { 0, 3 }, // CLEAR_GRASS, density
424 { 0, 7 }, // CLEAR_ROUGH, "random" based on position
425 { 0, 1 }, // CLEAR_ROCKS, tile hash parity
426 { 0, 7 }, // CLEAR_FIELDS, some field types
427 { 0, 3 }, // CLEAR_SNOW, density
428 { 1, 3 }, // CLEAR_DESERT, density
430 for (uint s = 0; s <= SLOPE_ELEVATED; s++) {
431 for (uint cg = 0; cg < 6; cg++) {
432 for (uint m = multi[cg].min; m <= multi[cg].max; m++) {
433 _vp_map_vegetation_clear_colours[s][cg][m] = GetSpriteMainColour(GetSpriteIDForClearGround((ClearGround)cg, (Slope)s, m), PAL_NONE);
438 /* Trees. */
439 extern uint32 _vp_map_vegetation_tree_colours[5][MAX_TREE_COUNT_BY_LANDSCAPE];
440 const uint base = _tree_base_by_landscape[_settings_game.game_creation.landscape];
441 const uint count = _tree_count_by_landscape[_settings_game.game_creation.landscape];
442 for (uint tg = 0; tg < 5; tg++) {
443 for (uint i = base; i < base + count; i++) {
444 _vp_map_vegetation_tree_colours[tg][i - base] = GetSpriteMainColour(_tree_sprites[i].sprite, _tree_sprites[i].pal);
446 const int diff = MAX_TREE_COUNT_BY_LANDSCAPE - count;
447 if (diff > 0) {
448 for (uint i = count; i < MAX_TREE_COUNT_BY_LANDSCAPE; i++)
449 _vp_map_vegetation_tree_colours[tg][i] = _vp_map_vegetation_tree_colours[tg][i - count];
454 /** Initialise and load all the sprites. */
455 void GfxLoadSprites()
457 DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
459 _grf_bug_too_many_strings = false;
461 SwitchNewGRFBlitter();
462 ClearFontCache();
463 GfxInitSpriteMem();
464 LoadSpriteTables();
465 GfxInitPalettes();
466 GfxDetermineMainColours();
468 UpdateRouteStepSpriteSize();
469 UpdateCursorSize();
471 DEBUG(sprite, 2, "Completed loading sprite set %d", _settings_game.game_creation.landscape);
474 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
476 bool ret = this->BaseSet<GraphicsSet, MAX_GFT, true>::FillSetDetails(ini, path, full_filename, false);
477 if (ret) {
478 IniGroup *metadata = ini->GetGroup("metadata");
479 IniItem *item;
481 fetch_metadata("palette");
482 this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
484 /* Get optional blitter information. */
485 item = metadata->GetItem("blitter", false);
486 this->blitter = (item != nullptr && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
488 return ret;
492 * Calculate and check the MD5 hash of the supplied GRF.
493 * @param file The file get the hash of.
494 * @param subdir The sub directory to get the files from.
495 * @return
496 * - #CR_MATCH if the MD5 hash matches
497 * - #CR_MISMATCH if the MD5 does not match
498 * - #CR_NO_FILE if the file misses
500 /* static */ MD5File::ChecksumResult GraphicsSet::CheckMD5(const MD5File *file, Subdirectory subdir)
502 size_t size = 0;
503 FILE *f = FioFOpenFile(file->filename, "rb", subdir, &size);
504 if (f == nullptr) return MD5File::CR_NO_FILE;
506 size_t max = GRFGetSizeOfDataSection(f);
508 FioFCloseFile(f);
510 return file->CheckMD5(subdir, max);
515 * Calculate and check the MD5 hash of the supplied filename.
516 * @param subdir The sub directory to get the files from
517 * @param max_size Only calculate the hash for this many bytes from the file start.
518 * @return
519 * - #CR_MATCH if the MD5 hash matches
520 * - #CR_MISMATCH if the MD5 does not match
521 * - #CR_NO_FILE if the file misses
523 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) const
525 size_t size;
526 FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
528 if (f == nullptr) return CR_NO_FILE;
530 size = min(size, max_size);
532 Md5 checksum;
533 uint8 buffer[1024];
534 uint8 digest[16];
535 size_t len;
537 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
538 size -= len;
539 checksum.Append(buffer, len);
542 FioFCloseFile(f);
544 checksum.Finish(digest);
545 return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
548 /** Names corresponding to the GraphicsFileType */
549 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
551 /** Implementation */
552 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
553 /* static */ const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _graphics_file_names;
555 template <class Tbase_set>
556 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
558 if (BaseMedia<Tbase_set>::used_set != nullptr) return true;
560 const Tbase_set *best = nullptr;
561 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) {
562 /* Skip unusable sets */
563 if (c->GetNumMissing() != 0) continue;
565 if (best == nullptr ||
566 (best->fallback && !c->fallback) ||
567 best->valid_files < c->valid_files ||
568 (best->valid_files == c->valid_files && (
569 (best->shortname == c->shortname && best->version < c->version) ||
570 (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
571 best = c;
575 BaseMedia<Tbase_set>::used_set = best;
576 return BaseMedia<Tbase_set>::used_set != nullptr;
579 template <class Tbase_set>
580 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
582 return ".obg"; // OpenTTD Base Graphics
585 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)