# 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 #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)