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   /*
00150    * Load additional sprites for climates other than temperate.
00151    * This overwrites some of the temperate sprites, such as foundations
00152    * and the ground sprites.
00153    */
00154   if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
00155     _palette_remap_grf[i] = (PAL_DOS != used_set->palette);
00156     LoadGrfIndexed(
00157       used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
00158       _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
00159       i++
00160     );
00161   }
00162 
00163   /* Initialize the unicode to sprite mapping table */
00164   InitializeUnicodeGlyphMap();
00165 
00166   /*
00167    * Load the base NewGRF with OTTD required graphics as first NewGRF.
00168    * However, we do not want it to show up in the list of used NewGRFs,
00169    * so we have to manually add it, and then remove it later.
00170    */
00171   GRFConfig *top = _grfconfig;
00172   GRFConfig *master = new GRFConfig(used_set->files[GFT_EXTRA].filename);
00173 
00174   /* We know the palette of the base set, so if the base NewGRF is not
00175    * setting one, use the palette of the base set and not the global
00176    * one which might be the wrong palette for this base NewGRF.
00177    * The value set here might be overridden via action14 later. */
00178   switch (used_set->palette) {
00179     case PAL_DOS:     master->palette |= GRFP_GRF_DOS;     break;
00180     case PAL_WINDOWS: master->palette |= GRFP_GRF_WINDOWS; break;
00181     default: break;
00182   }
00183   FillGRFDetails(master, false);
00184 
00185   ClrBit(master->flags, GCF_INIT_ONLY);
00186   master->next = top;
00187   _grfconfig = master;
00188 
00189   LoadNewGRF(SPR_NEWGRFS_BASE, i);
00190 
00191   /* Free and remove the top element. */
00192   delete master;
00193   _grfconfig = top;
00194 }
00195 
00196 
00198 void GfxLoadSprites()
00199 {
00200   DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
00201 
00202   GfxInitSpriteMem();
00203   LoadSpriteTables();
00204   GfxInitPalettes();
00205 
00206   UpdateCursorSize();
00207 }
00208 
00209 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
00210 {
00211   bool ret = this->BaseSet<GraphicsSet, MAX_GFT, DATA_DIR>::FillSetDetails(ini, path, full_filename, false);
00212   if (ret) {
00213     IniGroup *metadata = ini->GetGroup("metadata");
00214     IniItem *item;
00215 
00216     fetch_metadata("palette");
00217     this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00218   }
00219   return ret;
00220 }
00221 
00222 
00231 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
00232 {
00233   size_t size;
00234   FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
00235 
00236   if (f == NULL) return CR_NO_FILE;
00237 
00238   Md5 checksum;
00239   uint8 buffer[1024];
00240   uint8 digest[16];
00241   size_t len;
00242 
00243   while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00244     size -= len;
00245     checksum.Append(buffer, len);
00246   }
00247 
00248   FioFCloseFile(f);
00249 
00250   checksum.Finish(digest);
00251   return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
00252 }
00253 
00255 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00256 
00258 template <class T, size_t Tnum_files, Subdirectory Tsubdir>
00259 /* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _graphics_file_names;
00260 
00261 template <class Tbase_set>
00262 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
00263 {
00264   if (BaseMedia<Tbase_set>::used_set != NULL) return true;
00265 
00266   const Tbase_set *best = NULL;
00267   for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
00268     /* Skip unuseable sets */
00269     if (c->GetNumMissing() != 0) continue;
00270 
00271     if (best == NULL ||
00272         (best->fallback && !c->fallback) ||
00273         best->valid_files < c->valid_files ||
00274         (best->valid_files == c->valid_files && (
00275           (best->shortname == c->shortname && best->version < c->version) ||
00276           (best->palette != PAL_DOS && c->palette == PAL_DOS)))) {
00277       best = c;
00278     }
00279   }
00280 
00281   BaseMedia<Tbase_set>::used_set = best;
00282   return BaseMedia<Tbase_set>::used_set != NULL;
00283 }
00284 
00285 template <class Tbase_set>
00286 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
00287 {
00288   return ".obg"; // OpenTTD Base Graphics
00289 }
00290 
00291 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)

Generated on Fri May 27 04:19:42 2011 for OpenTTD by  doxygen 1.6.1