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