gfxinit.cpp

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

Generated on Fri Mar 4 21:36:59 2011 for OpenTTD by  doxygen 1.6.1