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)