gfxinit.cpp

Go to the documentation of this file.
00001 /* $Id$ */
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 "blitter/factory.hpp"
00019 #include "video/video_driver.hpp"
00020 
00021 /* The type of set we're replacing */
00022 #define SET_TYPE "graphics"
00023 #include "base_media_func.h"
00024 
00025 #include "table/sprites.h"
00026 #include "table/strings.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   while (LoadNextSprite(load_index, file_index, sprite_id)) {
00057     load_index++;
00058     sprite_id++;
00059     if (load_index >= MAX_SPRITES) {
00060       usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
00061     }
00062   }
00063   DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
00064 
00065   return load_index - load_index_org;
00066 }
00067 
00075 static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
00076 {
00077   uint start;
00078   uint sprite_id = 0;
00079 
00080   FioOpenFile(file_index, filename, BASESET_DIR);
00081 
00082   DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
00083 
00084   while ((start = *index_tbl++) != END) {
00085     uint end = *index_tbl++;
00086 
00087     do {
00088       bool b = LoadNextSprite(start, file_index, sprite_id);
00089       assert(b);
00090       sprite_id++;
00091     } while (++start <= end);
00092   }
00093 }
00094 
00100 void CheckExternalFiles()
00101 {
00102   if (BaseGraphics::GetUsedSet() == NULL || BaseSounds::GetUsedSet() == NULL) return;
00103 
00104   const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00105 
00106   DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
00107 
00108   static const size_t ERROR_MESSAGE_LENGTH = 256;
00109   static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
00110 
00111   /* Allocate for a message for each missing file and for one error
00112    * message per set.
00113    */
00114   char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
00115   error_msg[0] = '\0';
00116   char *add_pos = error_msg;
00117   const char *last = lastof(error_msg);
00118 
00119   if (used_set->GetNumInvalid() != 0) {
00120     /* Not all files were loaded successfully, see which ones */
00121     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);
00122     for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
00123       MD5File::ChecksumResult res = used_set->files[i].CheckMD5(BASESET_DIR);
00124       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);
00125     }
00126     add_pos += seprintf(add_pos, last, "\n");
00127   }
00128 
00129   const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
00130   if (sounds_set->GetNumInvalid() != 0) {
00131     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);
00132 
00133     assert_compile(SoundsSet::NUM_FILES == 1);
00134     /* No need to loop each file, as long as there is only a single
00135      * sound file. */
00136     add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", sounds_set->files->filename, sounds_set->files->CheckMD5(BASESET_DIR) == MD5File::CR_MISMATCH ? "corrupt" : "missing", sounds_set->files->missing_warning);
00137   }
00138 
00139   if (add_pos != error_msg) ShowInfoF("%s", error_msg);
00140 }
00141 
00143 static void LoadSpriteTables()
00144 {
00145   memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
00146   uint i = FIRST_GRF_SLOT;
00147   const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
00148 
00149   _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00150   LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
00151 
00152   /*
00153    * The second basic file always starts at the given location and does
00154    * contain a different amount of sprites depending on the "type"; DOS
00155    * has a few sprites less. However, we do not care about those missing
00156    * sprites as they are not shown anyway (logos in intro game).
00157    */
00158   _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00159   LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
00160 
00161   /*
00162    * Load additional sprites for climates other than temperate.
00163    * This overwrites some of the temperate sprites, such as foundations
00164    * and the ground sprites.
00165    */
00166   if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
00167     _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00168     LoadGrfFileIndexed(
00169       used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
00170       _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
00171       i++
00172     );
00173   }
00174 
00175   /* Initialize the unicode to sprite mapping table */
00176   InitializeUnicodeGlyphMap();
00177 
00178   /*
00179    * Load the base NewGRF with OTTD required graphics as first NewGRF.
00180    * However, we do not want it to show up in the list of used NewGRFs,
00181    * so we have to manually add it, and then remove it later.
00182    */
00183   GRFConfig *top = _grfconfig;
00184   GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
00185 
00186   /* We know the palette of the base set, so if the base NewGRF is not
00187    * setting one, use the palette of the base set and not the global
00188    * one which might be the wrong palette for this base NewGRF.
00189    * The value set here might be overridden via action14 later. */
00190   switch (used_set->palette) {
00191     case PAL_DOS:     master->palette |= GRFP_GRF_DOS;     break;
00192     case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
00193     default: break;
00194   }
00195   FillGRFDetails(master, false, BASESET_DIR);
00196 
00197   ClrBit(master->flags, GCF_INIT_ONLY);
00198   master->next = top;
00199   _grfconfig = master;
00200 
00201   LoadNewGRF(SPR_NEWGRFS_BASE, i);
00202 
00203   /* Free and remove the top element. */
00204   delete master;
00205   _grfconfig = top;
00206 }
00207 
00208 
00212 static void SwitchNewGRFBlitter()
00213 {
00214   /* Get blitter of base set. */
00215   bool is_32bpp = BaseGraphics::GetUsedSet()->blitter == BLT_32BPP;
00216 
00217   /* Get combined blitter mode of all NewGRFs. */
00218   for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
00219     if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND || HasBit(c->flags, GCF_INIT_ONLY)) continue;
00220 
00221     if (c->palette & GRFP_BLT_32BPP) is_32bpp = true;
00222   }
00223 
00224   /* A GRF would like a 32 bpp blitter, switch blitter if needed. Never switch if the blitter was specified by the user. */
00225   if (_blitter_autodetected && is_32bpp && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() != 0 && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() < 16) {
00226     const char *cur_blitter = BlitterFactoryBase::GetCurrentBlitter()->GetName();
00227     if (BlitterFactoryBase::SelectBlitter("32bpp-anim") != NULL) {
00228       if (!_video_driver->AfterBlitterChange()) {
00229         /* Failed to switch blitter, let's hope we can return to the old one. */
00230         if (BlitterFactoryBase::SelectBlitter(cur_blitter) == NULL || !_video_driver->AfterBlitterChange()) usererror("Failed to reinitialize video driver for 32 bpp blitter. Specify a fixed blitter in the config");
00231       }
00232     }
00233   }
00234 }
00235 
00237 void GfxLoadSprites()
00238 {
00239   DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
00240 
00241   SwitchNewGRFBlitter();
00242   GfxInitSpriteMem();
00243   LoadSpriteTables();
00244   GfxInitPalettes();
00245 
00246   UpdateCursorSize();
00247 }
00248 
00249 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
00250 {
00251   bool ret = this->BaseSet<GraphicsSet, MAX_GFT, true>::FillSetDetails(ini, path, full_filename, false);
00252   if (ret) {
00253     IniGroup *metadata = ini->GetGroup("metadata");
00254     IniItem *item;
00255 
00256     fetch_metadata("palette");
00257     this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00258 
00259     /* Get optional blitter information. */
00260     item = metadata->GetItem("blitter", false);
00261     this->blitter = (item != NULL && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
00262   }
00263   return ret;
00264 }
00265 
00266 
00275 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
00276 {
00277   size_t size;
00278   FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
00279 
00280   if (f == NULL) return CR_NO_FILE;
00281 
00282   Md5 checksum;
00283   uint8 buffer[1024];
00284   uint8 digest[16];
00285   size_t len;
00286 
00287   while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00288     size -= len;
00289     checksum.Append(buffer, len);
00290   }
00291 
00292   FioFCloseFile(f);
00293 
00294   checksum.Finish(digest);
00295   return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
00296 }
00297 
00299 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00300 
00302 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
00303 /* static */ const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _graphics_file_names;
00304 
00305 template <class Tbase_set>
00306 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
00307 {
00308   if (BaseMedia<Tbase_set>::used_set != NULL) return true;
00309 
00310   const Tbase_set *best = NULL;
00311   for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
00312     /* Skip unuseable sets */
00313     if (c->GetNumMissing() != 0) continue;
00314 
00315     if (best == NULL ||
00316         (best->fallback && !c->fallback) ||
00317         best->valid_files < c->valid_files ||
00318         (best->valid_files == c->valid_files && (
00319           (best->shortname == c->shortname && best->version < c->version) ||
00320           (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
00321       best = c;
00322     }
00323   }
00324 
00325   BaseMedia<Tbase_set>::used_set = best;
00326   return BaseMedia<Tbase_set>::used_set != NULL;
00327 }
00328 
00329 template <class Tbase_set>
00330 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
00331 {
00332   return ".obg"; // OpenTTD Base Graphics
00333 }
00334 
00335 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)