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