# gfxinit.cpp

Go to the documentation of this file.

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