gfxinit.cpp

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