# gfxinit.cpp

Go to the documentation of this file.

    00001 /* $Id: gfxinit.cpp 20430 2010-08-09 21:48:19Z 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   UpdateCursorSize();
    00206 }
    00207
    00208 bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
    00209 {
    00210   bool ret = this->BaseSet<GraphicsSet, MAX_GFT, DATA_DIR>::FillSetDetails(ini, path, full_filename, false);
    00211   if (ret) {
    00212     IniGroup *metadata = ini->GetGroup("metadata");
    00213     IniItem *item;
    00214
    00215     fetch_metadata("palette");
    00216     this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
    00217   }
    00218   return ret;
    00219 }
    00220
    00221
    00230 MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
    00231 {
    00232   size_t size;
    00233   FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
    00234
    00235   if (f == NULL) return CR_NO_FILE;
    00236
    00237   Md5 checksum;
    00238   uint8 buffer[1024];
    00239   uint8 digest[16];
    00240   size_t len;
    00241
    00242   while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
    00243     size -= len;
    00244     checksum.Append(buffer, len);
    00245   }
    00246
    00247   FioFCloseFile(f);
    00248
    00249   checksum.Finish(digest);
    00250   return memcmp(this->hash, digest, sizeof(this->hash)) == 0 ? CR_MATCH : CR_MISMATCH;
    00251 }
    00252
    00254 static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
    00255
    00257 template <class T, size_t Tnum_files, Subdirectory Tsubdir>
    00258 /* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _graphics_file_names;
    00259
    00260 extern void UpdateNewGRFConfigPalette();
    00261
    00267 /* static */ void BaseGraphics::DeterminePalette()
    00268 {
    00269   assert(BaseGraphics::used_set != NULL);
    00270   if (_use_palette >= MAX_PAL) _use_palette = BaseGraphics::used_set->palette;
    00271
    00272   switch (_use_palette) {
    00273     case PAL_DOS:
    00274       _palette_remap = _palmap_w2d;
    00275       _palette_reverse_remap = _palmap_d2w;
    00276       break;
    00277
    00278     case PAL_WINDOWS:
    00279       _palette_remap = _palmap_d2w;
    00280       _palette_reverse_remap = _palmap_w2d;
    00281       break;
    00282
    00283     default:
    00284       NOT_REACHED();
    00285   }
    00286
    00287   UpdateNewGRFConfigPalette();
    00288 }
    00289
    00290 template <class Tbase_set>
    00291 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
    00292 {
    00293   if (BaseMedia<Tbase_set>::used_set != NULL) return true;
    00294
    00295   const Tbase_set *best = NULL;
    00296   for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
    00297     /* Skip unuseable sets */
    00298     if (c->GetNumMissing() != 0) continue;
    00299
    00300     if (best == NULL ||
    00301         (best->fallback && !c->fallback) ||
    00302         best->valid_files < c->valid_files ||
    00303         (best->valid_files == c->valid_files && (
    00304           (best->shortname == c->shortname && best->version < c->version) ||
    00305           (best->palette != _use_palette && c->palette == _use_palette)))) {
    00306       best = c;
    00307     }
    00308   }
    00309
    00310   BaseMedia<Tbase_set>::used_set = best;
    00311   return BaseMedia<Tbase_set>::used_set != NULL;
    00312 }
    00313
    00314 template <class Tbase_set>
    00315 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
    00316 {
    00317   return ".obg"; // OpenTTD Base Graphics
    00318 }
    00319
    00320 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<GraphicsSet>, GraphicsSet)