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