# 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
    00019 /* The type of set we're replacing */
    00020 #define SET_TYPE "graphics"
    00021 #include "base_media_func.h"
    00022
    00023 #include "table/sprites.h"
    00024
    00026 bool _palette_remap_grf[MAX_FILE_SLOTS];
    00027
    00028 #include "table/landscape_sprite.h"
    00029
    00030 static const SpriteID * const _landscape_spriteindexes[] = {
    00031   _landscape_spriteindexes_1,
    00032   _landscape_spriteindexes_2,
    00033   _landscape_spriteindexes_3,
    00034 };
    00035
    00036 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
    00037 {
    00038   uint load_index_org = load_index;
    00039   uint sprite_id = 0;
    00040
    00041   FioOpenFile(file_index, filename);
    00042
    00043   DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
    00044
    00045   while (LoadNextSprite(load_index, file_index, sprite_id)) {
    00046     load_index++;
    00047     sprite_id++;
    00048     if (load_index >= MAX_SPRITES) {
    00049       usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
    00050     }
    00051   }
    00052   DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
    00053
    00054   return load_index - load_index_org;
    00055 }
    00056
    00057
    00058 static void LoadSpritesIndexed(int file_index, uint *sprite_id, const SpriteID *index_tbl)
    00059 {
    00060   uint start;
    00061   while ((start = *index_tbl++) != END) {
    00062     uint end = *index_tbl++;
    00063
    00064     do {
    00065       bool b = LoadNextSprite(start, file_index, *sprite_id);
    00066       assert(b);
    00067       (*sprite_id)++;
    00068     } while (++start <= end);
    00069   }
    00070 }
    00071
    00072 static void LoadGrfIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
    00073 {
    00074   uint sprite_id = 0;
    00075
    00076   FioOpenFile(file_index, filename);
    00077
    00078   DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
    00079
    00080   LoadSpritesIndexed(file_index, &sprite_id, index_tbl);
    00081 }
    00082
    00088 void CheckExternalFiles()
    00089 {
    00090   if (BaseGraphics::GetUsedSet() == NULL || BaseSounds::GetUsedSet() == NULL) return;
    00091
    00092   const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
    00093
    00094   DEBUG(grf, 1, "Using the %s base graphics set", used_set->name);
    00095
    00096   static const size_t ERROR_MESSAGE_LENGTH = 256;
    00097   static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
    00098
    00099   /* Allocate for a message for each missing file and for one error
    00100    * message per set.
    00101    */
    00102   char error_msg[MISSING_FILE_MESSAGE_LENGTH * (GraphicsSet::NUM_FILES + SoundsSet::NUM_FILES) + 2 * ERROR_MESSAGE_LENGTH];
    00103   error_msg[0] = '\0';
    00104   char *add_pos = error_msg;
    00105   const char *last = lastof(error_msg);
    00106
    00107   if (used_set->GetNumInvalid() != 0) {
    00108     /* Not all files were loaded successfully, see which ones */
    00109     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);
    00110     for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
    00111       MD5File::ChecksumResult res = used_set->files[i].CheckMD5(DATA_DIR);
    00112       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);
    00113     }
    00114     add_pos += seprintf(add_pos, last, "\n");
    00115   }
    00116
    00117   const SoundsSet *sounds_set = BaseSounds::GetUsedSet();
    00118   if (sounds_set->GetNumInvalid() != 0) {
    00119     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);
    00120
    00121     assert_compile(SoundsSet::NUM_FILES == 1);
    00122     /* No need to loop each file, as long as there is only a single
    00123      * sound file. */
    00124     add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", sounds_set->files->filename, sounds_set->files->CheckMD5(DATA_DIR) == MD5File::CR_MISMATCH ? "corrupt" : "missing", sounds_set->files->missing_warning);
    00125   }
    00126
    00127   if (add_pos != error_msg) ShowInfoF("%s", error_msg);
    00128 }
    00129
    00131 static void LoadSpriteTables()
    00132 {
    00133   memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
    00134   uint i = FIRST_GRF_SLOT;
    00135   const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
    00136
    00137   _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
    00138   LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
    00139
    00140   /*
    00141    * The second basic file always starts at the given location and does
    00142    * contain a different amount of sprites depending on the "type"; DOS
    00143    * has a few sprites less. However, we do not care about those missing
    00144    * sprites as they are not shown anyway (logos in intro game).
    00145    */
    00146   _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
    00147   LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
    00148
    00149   /* Blacktileset comlete all slopes. */
    00150   LoadGrfFile("flat_blacktiles.grf", SPR_FLAT_BLACKTILES, i++);
    00151
    00152   /*
    00153    * Load additional sprites for climates other than temperate.
    00154    * This overwrites some of the temperate sprites, such as foundations
    00155    * and the ground sprites.
    00156    */
    00157   if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
    00158     _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
    00159     LoadGrfIndexed(
    00160       used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
    00161       _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
    00162       i++
    00163     );
    00164   }
    00165
    00166   /* Initialize the unicode to sprite mapping table */
    00167   InitializeUnicodeGlyphMap();
    00168
    00169   /* Load CopyPaste graphics. */
    00170   LoadGrfFile("copypaste.grf", SPR_COPYPASTE_BASE, i++);
    00171
    00172   /* Load traffic lights graphics. */
    00173   LoadGrfFile("trafficlights.grf", SPR_TRAFFICLIGHTS_BASE, i++);
    00174
    00175     /* load speedsign graphcs. */
    00176   LoadGrfFile("speedsignals.grf", SPR_SPEEDSIGNAL_BASE, i++);
    00177
    00178     /* load speedsign graphcs. */
    00179   LoadGrfFile("progsignals.grf", SPR_PROGSIGNAL_BASE, i++);
    00180   /*
    00181    * Load the base NewGRF with OTTD required graphics as first NewGRF.
    00182    * However, we do not want it to show up in the list of used NewGRFs,
    00183    * so we have to manually add it, and then remove it later.
    00184    */
    00185   GRFConfig *top = _grfconfig;
    00186   GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
    00187
    00188   /* We know the palette of the base set, so if the base NewGRF is not
    00189    * setting one, use the palette of the base set and not the global
    00190    * one which might be the wrong palette for this base NewGRF.
    00191    * The value set here might be overridden via action14 later. */
    00192   switch (used_set->palette) {
    00193     case PAL_DOS:     master->palette |= GRFP_GRF_DOS;     break;
    00194     case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
    00195     default: break;
    00196   }
    00197   FillGRFDetails(master, false);
    00198
    00199   ClrBit(master->flags, GCF_INIT_ONLY);
    00200   master->next = top;
    00201   _grfconfig = master;
    00202
    00203   LoadNewGRF(SPR_NEWGRFS_BASE, i);
    00204
    00205   /* Free and remove the top element. */
    00206   delete master;
    00207   _grfconfig = top;
    00208 }
    00209
    00210
    00212 void GfxLoadSprites()
    00213 {
    00214   DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
    00215
    00216   GfxInitSpriteMem();
    00217   LoadSpriteTables();
    00218   GfxInitPalettes();
    00219
    00220   UpdateCursorSize();
    00221 }
    00222
    00223 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
    00224 {
    00225   bool ret = this->BaseSet<GraphicsSet, MAX_GFT, DATA_DIR>::FillSetDetails(ini, path, full_filename, false);
    00226   if (ret) {
    00227     IniGroup *metadata = ini->GetGroup("metadata");
    00228     IniItem *item;
    00229
    00230     fetch_metadata("palette");
    00231     this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
    00232   }
    00233   return ret;
    00234 }
    00235
    00236
    00245 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
    00246 {
    00247   size_t size;
    00248   FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
    00249
    00250   if (f == NULL) return CR_NO_FILE;
    00251
    00252   Md5 checksum;
    00253   uint8 buffer[1024];
    00254   uint8 digest[16];
    00255   size_t len;
    00256
    00257   while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
    00258     size -= len;
    00259     checksum.Append(buffer, len);
    00260   }
    00261
    00262   FioFCloseFile(f);
    00263
    00264   checksum.Finish(digest);
    00265   return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
    00266 }
    00267
    00269 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
    00270
    00272 template <class T, size_t Tnum_files, Subdirectory Tsubdir>
    00273 /* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _graphics_file_names;
    00274
    00275 template <class Tbase_set>
    00276 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
    00277 {
    00278   if (BaseMedia<Tbase_set>::used_set != NULL) return true;
    00279
    00280   const Tbase_set *best = NULL;
    00281   for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
    00282     /* Skip unuseable sets */
    00283     if (c->GetNumMissing() != 0) continue;
    00284
    00285     if (best == NULL ||
    00286         (best->fallback && !c->fallback) ||
    00287         best->valid_files < c->valid_files ||
    00288         (best->valid_files == c->valid_files && (
    00289           (best->shortname == c->shortname && best->version < c->version) ||
    00290           (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
    00291       best = c;
    00292     }
    00293   }
    00294
    00295   BaseMedia<Tbase_set>::used_set = best;
    00296   return BaseMedia<Tbase_set>::used_set != NULL;
    00297 }
    00298
    00299 template <class Tbase_set>
    00300 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
    00301 {
    00302   return ".obg"; // OpenTTD Base Graphics
    00303 }
    00304
    00305 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)