# gfxinit.cpp

Go to the documentation of this file.

    00001 /* $Id: gfxinit.cpp 26544 2014-04-29 18:41:19Z frosch $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "fios.h"
    00014 #include "newgrf.h"
    00015 #include "3rdparty/md5/md5.h"
    00016 #include "fontcache.h"
    00017 #include "gfx_func.h"
    00018 #include "transparency.h"
    00019 #include "blitter/factory.hpp"
    00020 #include "video/video_driver.hpp"
    00021
    00022 /* The type of set we're replacing */
    00023 #define SET_TYPE "graphics"
    00024 #include "base_media_func.h"
    00025
    00026 #include "table/sprites.h"
    00027
    00029 bool _palette_remap_grf[MAX_FILE_SLOTS];
    00030
    00031 #include "table/landscape_sprite.h"
    00032
    00034 static const SpriteID * const _landscape_spriteindexes[] = {
    00035   _landscape_spriteindexes_arctic,
    00036   _landscape_spriteindexes_tropic,
    00037   _landscape_spriteindexes_toyland,
    00038 };
    00039
    00047 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
    00048 {
    00049   uint load_index_org = load_index;
    00050   uint sprite_id = 0;
    00051
    00052   FioOpenFile(file_index, filename, BASESET_DIR);
    00053
    00054   DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
    00055
    00056   byte container_ver = GetGRFContainerVersion();
    00057   if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
    00058   ReadGRFSpriteOffsets(container_ver);
    00059   if (container_ver >= 2) {
    00060     /* Read compression. */
    00061     byte compression = FioReadByte();
    00062     if (compression != 0) usererror("Unsupported compression format");
    00063   }
    00064
    00065   while (LoadNextSprite(load_index, file_index, sprite_id, container_ver)) {
    00066     load_index++;
    00067     sprite_id++;
    00068     if (load_index >= MAX_SPRITES) {
    00069       usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
    00070     }
    00071   }
    00072   DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
    00073
    00074   return load_index - load_index_org;
    00075 }
    00076
    00084 static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
    00085 {
    00086   uint start;
    00087   uint sprite_id = 0;
    00088
    00089   FioOpenFile(file_index, filename, BASESET_DIR);
    00090
    00091   DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
    00092
    00093   byte container_ver = GetGRFContainerVersion();
    00094   if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
    00095   ReadGRFSpriteOffsets(container_ver);
    00096   if (container_ver >= 2) {
    00097     /* Read compression. */
    00098     byte compression = FioReadByte();
    00099     if (compression != 0) usererror("Unsupported compression format");
    00100   }
    00101
    00102   while ((start = *index_tbl++) != END) {
    00103     uint end = *index_tbl++;
    00104
    00105     do {
    00106       bool b = LoadNextSprite(start, file_index, sprite_id, container_ver);
    00107       assert(b);
    00108       sprite_id++;
    00109     } while (++start <= end);
    00110   }
    00111 }
    00112
    00118 void CheckExternalFiles()
    00119 {
    00120   if (BaseGraphics::GetUsedSet() == NULL || BaseSounds::GetUsedSet() == NULL) return;
    00121
    00122   const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
    00123
    00124   DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
    00125
    00126   static const size_t ERROR_MESSAGE_LENGTH = 256;
    00127   static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
    00128
    00129   /* Allocate for a message for each missing file and for one error
    00130    * message per set.
    00131    */
    00132   char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
    00133   error_msg[0] = '\0';
    00134   char *add_pos = error_msg;
    00135   const char *last = lastof(error_msg);
    00136
    00137   if (used_set->GetNumInvalid() != 0) {
    00138     /* Not all files were loaded successfully, see which ones */
    00139     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);
    00140     for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
    00141       MD5File::ChecksumResult res = GraphicsSet::CheckMD5(&used_set->files[i], BASESET_DIR);
    00142       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);
    00143     }
    00144     add_pos += seprintf(add_pos, last, "\n");
    00145   }
    00146
    00147   const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
    00148   if (sounds_set->GetNumInvalid() != 0) {
    00149     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);
    00150
    00151     assert_compile(SoundsSet::NUM_FILES == 1);
    00152     /* No need to loop each file, as long as there is only a single
    00153      * sound file. */
    00154     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);
    00155   }
    00156
    00157   if (add_pos != error_msg) ShowInfoF("%s", error_msg);
    00158 }
    00159
    00161 static void LoadSpriteTables()
    00162 {
    00163   memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
    00164   uint i = FIRST_GRF_SLOT;
    00165   const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
    00166
    00167   _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
    00168   LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
    00169
    00170   /*
    00171    * The second basic file always starts at the given location and does
    00172    * contain a different amount of sprites depending on the "type"; DOS
    00173    * has a few sprites less. However, we do not care about those missing
    00174    * sprites as they are not shown anyway (logos in intro game).
    00175    */
    00176   _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
    00177   LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
    00178
    00179   /*
    00180    * Load additional sprites for climates other than temperate.
    00181    * This overwrites some of the temperate sprites, such as foundations
    00182    * and the ground sprites.
    00183    */
    00184   if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
    00185     _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
    00186     LoadGrfFileIndexed(
    00187       used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
    00188       _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
    00189       i++
    00190     );
    00191   }
    00192
    00193   /* Initialize the unicode to sprite mapping table */
    00194   InitializeUnicodeGlyphMap();
    00195
    00196   /*
    00197    * Load the base NewGRF with OTTD required graphics as first NewGRF.
    00198    * However, we do not want it to show up in the list of used NewGRFs,
    00199    * so we have to manually add it, and then remove it later.
    00200    */
    00201   GRFConfig *top = _grfconfig;
    00202   GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
    00203
    00204   /* We know the palette of the base set, so if the base NewGRF is not
    00205    * setting one, use the palette of the base set and not the global
    00206    * one which might be the wrong palette for this base NewGRF.
    00207    * The value set here might be overridden via action14 later. */
    00208   switch (used_set->palette) {
    00209     case PAL_DOS:     master->palette |= GRFP_GRF_DOS;     break;
    00210     case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
    00211     default: break;
    00212   }
    00213   FillGRFDetails(master, false, BASESET_DIR);
    00214
    00215   ClrBit(master->flags, GCF_INIT_ONLY);
    00216   master->next = top;
    00217   _grfconfig = master;
    00218
    00219   LoadNewGRF(SPR_NEWGRFS_BASE, i);
    00220
    00221   /* Free and remove the top element. */
    00222   delete master;
    00223   _grfconfig = top;
    00224 }
    00225
    00226
    00231 static bool SwitchNewGRFBlitter()
    00232 {
    00233   /* Never switch if the blitter was specified by the user. */
    00234   if (!_blitter_autodetected) return false;
    00235
    00236   /* Null driver => dedicated server => do nothing. */
    00237   if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) return false;
    00238
    00239   /* Get preferred depth. */
    00240   uint depth_wanted_by_base = BaseGraphics::GetUsedSet()->blitter == BLT_32BPP ? 32 : 8;
    00241   uint depth_wanted_by_grf = 8;
    00242   for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
    00243     if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND || HasBit(c->flags, GCF_INIT_ONLY)) continue;
    00244     if (c->palette & GRFP_BLT_32BPP) depth_wanted_by_grf = 32;
    00245   }
    00246
    00247   /* Search the best blitter. */
    00248   struct {
    00249     const char *name;
    00250     uint animation;
    00251     uint min_base_depth, max_base_depth, min_grf_depth, max_grf_depth;
    00252   } replacement_blitters[] = {
    00253 #ifdef WITH_SSE
    00254     { "32bpp-sse4",      0, 32, 32,  8, 32 },
    00255     { "32bpp-ssse3",     0, 32, 32,  8, 32 },
    00256     { "32bpp-sse2",      0, 32, 32,  8, 32 },
    00257     { "32bpp-sse4-anim", 1, 32, 32,  8, 32 },
    00258 #endif
    00259     { "8bpp-optimized",  2,  8,  8,  8,  8 },
    00260     { "32bpp-optimized", 0,  8, 32,  8, 32 },
    00261     { "32bpp-anim",      1,  8, 32,  8, 32 },
    00262   };
    00263
    00264   const bool animation_wanted = HasBit(_display_opt, DO_FULL_ANIMATION);
    00265   const char *cur_blitter = BlitterFactory::GetCurrentBlitter()->GetName();
    00266
    00267   for (uint i = 0; i < lengthof(replacement_blitters); i++) {
    00268     if (animation_wanted && (replacement_blitters[i].animation == 0)) continue;
    00269     if (!animation_wanted && (replacement_blitters[i].animation == 1)) continue;
    00270
    00271     if (!IsInsideMM(depth_wanted_by_base, replacement_blitters[i].min_base_depth, replacement_blitters[i].max_base_depth + 1)) continue;
    00272     if (!IsInsideMM(depth_wanted_by_grf, replacement_blitters[i].min_grf_depth, replacement_blitters[i].max_grf_depth + 1)) continue;
    00273     const char *repl_blitter = replacement_blitters[i].name;
    00274
    00275     if (strcmp(repl_blitter, cur_blitter) == 0) return false;
    00276     if (BlitterFactory::GetBlitterFactory(repl_blitter) == NULL) continue;
    00277
    00278     DEBUG(misc, 1, "Switching blitter from '%s' to '%s'... ", cur_blitter, repl_blitter);
    00279     Blitter *new_blitter = BlitterFactory::SelectBlitter(repl_blitter);
    00280     if (new_blitter == NULL) NOT_REACHED();
    00281     DEBUG(misc, 1, "Successfully switched to %s.", repl_blitter);
    00282     break;
    00283   }
    00284
    00285   if (!VideoDriver::GetInstance()->AfterBlitterChange()) {
    00286     /* Failed to switch blitter, let's hope we can return to the old one. */
    00287     if (BlitterFactory::SelectBlitter(cur_blitter) == NULL || !VideoDriver::GetInstance()->AfterBlitterChange()) usererror("Failed to reinitialize video driver. Specify a fixed blitter in the config");
    00288   }
    00289
    00290   return true;
    00291 }
    00292
    00294 void CheckBlitter()
    00295 {
    00296   if (!SwitchNewGRFBlitter()) return;
    00297
    00298   ClearFontCache();
    00299   GfxClearSpriteCache();
    00300 }
    00301
    00303 void GfxLoadSprites()
    00304 {
    00305   DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
    00306
    00307   SwitchNewGRFBlitter();
    00308   ClearFontCache();
    00309   GfxInitSpriteMem();
    00310   LoadSpriteTables();
    00311   GfxInitPalettes();
    00312
    00313   UpdateCursorSize();
    00314 }
    00315
    00316 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
    00317 {
    00318   bool ret = this->BaseSet<GraphicsSet, MAX_GFT, true>::FillSetDetails(ini, path, full_filename, false);
    00319   if (ret) {
    00320     IniGroup *metadata = ini->GetGroup("metadata");
    00321     IniItem *item;
    00322
    00323     fetch_metadata("palette");
    00324     this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
    00325
    00326     /* Get optional blitter information. */
    00327     item = metadata->GetItem("blitter", false);
    00328     this->blitter = (item != NULL && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
    00329   }
    00330   return ret;
    00331 }
    00332
    00342 /* static */ MD5File::ChecksumResult GraphicsSet::CheckMD5(const MD5File *file, Subdirectory subdir)
    00343 {
    00344   size_t size = 0;
    00345   FILE *f = FioFOpenFile(file->filename, "rb", subdir, &size);
    00346   if (f == NULL) return MD5File::CR_NO_FILE;
    00347
    00348   size_t max = GRFGetSizeOfDataSection(f);
    00349
    00350   FioFCloseFile(f);
    00351
    00352   return file->CheckMD5(subdir, max);
    00353 }
    00354
    00355
    00365 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) const
    00366 {
    00367   size_t size;
    00368   FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
    00369
    00370   if (f == NULL) return CR_NO_FILE;
    00371
    00372   size = min(size, max_size);
    00373
    00374   Md5 checksum;
    00375   uint8 buffer[1024];
    00376   uint8 digest[16];
    00377   size_t len;
    00378
    00379   while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
    00380     size -= len;
    00381     checksum.Append(buffer, len);
    00382   }
    00383
    00384   FioFCloseFile(f);
    00385
    00386   checksum.Finish(digest);
    00387   return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
    00388 }
    00389
    00391 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
    00392
    00394 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
    00395 /* static */ const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _graphics_file_names;
    00396
    00397 template <class Tbase_set>
    00398 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
    00399 {
    00400   if (BaseMedia<Tbase_set>::used_set != NULL) return true;
    00401
    00402   const Tbase_set *best = NULL;
    00403   for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
    00404     /* Skip unusable sets */
    00405     if (c->GetNumMissing() != 0) continue;
    00406
    00407     if (best == NULL ||
    00408         (best->fallback && !c->fallback) ||
    00409         best->valid_files < c->valid_files ||
    00410         (best->valid_files == c->valid_files && (
    00411           (best->shortname == c->shortname && best->version < c->version) ||
    00412           (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
    00413       best = c;
    00414     }
    00415   }
    00416
    00417   BaseMedia<Tbase_set>::used_set = best;
    00418   return BaseMedia<Tbase_set>::used_set != NULL;
    00419 }
    00420
    00421 template <class Tbase_set>
    00422 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
    00423 {
    00424   return ".obg"; // OpenTTD Base Graphics
    00425 }
    00426
    00427 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)