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)